[Bug libstdc++/111351] constexpr std::string objects permitted to escape constant evaluation when SSO

2023-09-08 Thread arthur.j.odwyer at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111351

--- Comment #1 from Arthur O'Dwyer  ---
(Author of the blog post here.)
In contrast to James' view, I think the libstdc++/MSVC behavior is relatively
easy to explain; I think libc++'s `if consteval` approach is baroque and
confusing. [That is, _both_ behaviors are confusing to the newbie and need
expert explanation, but libc++'s choice is confusing even for the experts, who
have to maintain its split-brain SSO logic forever because Hyrum's Law. If you
have to maintain something forever, you should at least choose to make it
_simple_! As I say in the blog post, in hindsight I think libc++ screwed up
here.]

IMHO it is a feature, not a bug, that I can write these lines:

constinit std::string s1;
constinit std::vector v1;

libstdc++ would be within its rights, paper-Standard-wise, to reject both of
these lines; but I don't think libstdc++ _should_ reject either of them.
They're both fine code as far as I'm concerned. I think libc++ is the
user-hostile/broken implementation here, not libstdc++.

Anyone who thinks libstdc++ ought to reject `s1` above should at least be
forced to explain what libstdc++ ought to do about `v1`. From the
user-programmer's POV, there's no difference between a default-initialized
string and a default-initialized vector. Users don't care about these SSO
details; they just want the code to work. That's what libstdc++ currently does.
Good, IMO.

[Bug libstdc++/111351] constexpr std::string objects permitted to escape constant evaluation when SSO

2023-09-11 Thread de34 at live dot cn via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111351

Jiang An  changed:

   What|Removed |Added

 CC||de34 at live dot cn

--- Comment #2 from Jiang An  ---
> This will result in user code which can be built or not based on whether 
> their string happens to fit within the SSO string length. I find that quite 
> unfortunate, since it is supposed to be an internal implementation 
> detail/optimization, and this makes it effectively part of the API that code 
> will grow to depend on.

This comes from the nature of SSO and constexpr variables. I don't think it
worthwhile to revert this PR (https://github.com/microsoft/STL/pull/1735) for
MSVC STL - which makes the codes harder to understand and slows down the
compilation.

> As comparison, libc++ rejects all the above examples, by forcing the SSO-size 
> to zero in constant evaluation context, so that a pointer to an external 
> allocation is always used.


libc++ is, unfortunately, currently unable to implement constexpr SSO, IIUC. I
failed to find a constexpr-compatible way to determine libc++'s std::string is
in short mode if the short mode were enabled in constant evaluation. (As a
result, libc++'s string always uses long mode during constant evaluation.)

https://github.com/llvm/llvm-project/blob/0954dc3fb9214b994623f5306473de075f8e3593/libcxx/include/string#L829-L837

Note that there's no tag outside of the union, so we can't know which variant
member is active without causing constant evaluation failure - unless the
mechanism of std::is_within_lifetime (https://wg21.link/p2641r4) gets
implemented.

[Bug libstdc++/111351] constexpr std::string objects permitted to escape constant evaluation when SSO

2023-09-11 Thread redi at gcc dot gnu.org via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111351

--- Comment #3 from Jonathan Wakely  ---
Does using __builtin_is_constant_p on the union member not work?

[Bug libstdc++/111351] constexpr std::string objects permitted to escape constant evaluation when SSO

2023-09-11 Thread foom at fuhm dot net via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111351

--- Comment #4 from James Y Knight  ---
vector and string are different in one key way: a zero-sized vector has no
accessible storage, while a zero-sized string has 1 byte of readable storage --
the NUL terminator. Because of that, I don't think it's unreasonable for a
zero-length vector to be constinit'able, while a zero-length string is not.

But, certainly the _more_ concerning issue is with non-zero-sized strings where
the validity of the program depends on what exact SSO size was chosen by the
implementation.

> libc++'s choice is confusing even for the experts, who have to maintain its 
> split-brain SSO logic forever because Hyrum's Law

"Hyrum's Law" is exactly why I think it's a mistake to permit SSO-allocated
strings. It makes the SSO length a critical part of the interface. People will
start writing code which assumes that a constexpr global string of size "N"
works, and that will cause problems for other standard libraries which use a
different SSO size "M", if M < N. E.g. if libc++ starts allowing this, then
people who first target libc++ will find that strings up to 22 characters work,
and be surprised/annoyed by libstdc++ failing to build their program.

It is much simpler to say to users "you cannot make a constexpr std::string
unless it lives fully within constant-evaluation-time." then to also add "...OR
unless the size is short enough, where that size depends on your
implementation."

[Bug libstdc++/111351] constexpr std::string objects permitted to escape constant evaluation when SSO

2023-09-12 Thread foom at fuhm dot net via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111351

--- Comment #5 from James Y Knight  ---
> Does using __builtin_is_constant_p on the union member not work?

I've created a proof-of-concept patch for libc++ to support SSO strings during
constant evaluation. It works.

If everyone disagrees with me and believes that this is a really awesome
foot-gun to give to users, I will go ahead and propose that patch to libc++
maintainers. (As mentioned, that'll cause more code to be compilable under
libc++ than is possible to permit under libstdc++/MSVC implementations).

However, I continue to believe the opposite outcome, prohibiting this
everywhere, would be preferable.

[Bug libstdc++/111351] constexpr std::string objects permitted to escape constant evaluation when SSO

2023-09-12 Thread arthur.j.odwyer at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111351

--- Comment #6 from Arthur O'Dwyer  ---
(In reply to James Y Knight from comment #5)
> > Does using __builtin_is_constant_p on the union member not work?
> 
> I've created a proof-of-concept patch for libc++ to support SSO strings
> during constant evaluation. It works.
> 
> If everyone disagrees with me and believes that this is a really awesome
> foot-gun to give to users, I will go ahead and propose that patch to libc++
> maintainers. (As mentioned, that'll cause more code to be compilable under
> libc++ than is possible to permit under libstdc++/MSVC implementations).

FWIW #1: Personally I would be weakly in favor of that patch, but I would also
be pessimistic about its chances of getting accepted in the current libc++
climate.

FWIW #2: A worst-of-both-worlds option ;) would be for your patch to `if
consteval` the SSO buffer size so that it would be 24 at runtime (matching
libc++'s current behavior) but 16 at compile time (matching libstdc++ and
Microsoft if I'm not mistaken, so you'd get your cross-vendor portability at
compile time). *I* would still consider that an unnecessary-and-thus-bad
crippling of libc++ string's cool 24-byte-SSO feature; but I could imagine
someone else finding it more palatable than any other alternative. 
["Worst-of-both-worlds" in the sense that you're paying to change the code at
all, but the end result still has two codepaths that both need to be
maintained, and divergence between compile-time and runtime SSO behavior.]

[Bug libstdc++/111351] constexpr std::string objects permitted to escape constant evaluation when SSO

2023-09-26 Thread foom at fuhm dot net via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=111351

--- Comment #7 from James Y Knight  ---
On the libc++ side, a suggestion was given that instead of making this an
_error_, we could instead emit a warning if "a constexpr or constinit object is
a basic_string or contains a basic_string subobject, or the definition of a
constexpr or constinit variable extends the lifetime of a temporary object that
meets the previous condition."

I think that was a really great suggestion -- diagnosing via a warning is a
nicer solution than putting is_constant_evaluated hacks into the library (as
MSVC had, and libc++ currently has but will likely remove).

One could either hardcode std::basic_string for the diagnostic, or add a new
type-attribute to permit any type to opt-in to such a warning. You'd want to
use it if you have a type where you don't _intend_ to support
constant-initialization, but where it may sometimes be possible as an
implementation detail, and you want to tell users not to rely on that
implementation detail.