[Bug c++/104872] New: Memory corruption in Coroutine with POD type

2022-03-10 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104872

Bug ID: 104872
   Summary: Memory corruption in Coroutine with POD type
   Product: gcc
   Version: 11.2.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: benni.buch at gmail dot com
  Target Milestone: ---

The bug affects all versions of GCC 11 and trunk (upcoming version 12).

The following minimal example causes a crash during memory freeing. However,
the error seems to be caused by a previous incorrect memory access. At least
that is what the log output of contructors, the assignment and the destructor
suggests.

Tested with Compiler Explorer: https://godbolt.org/z/WKf57cPzn

```cpp
#include 
#include 
#include 


using namespace std::literals;


class logging_string{
public:
logging_string(std::string_view text) :text_(text) {
std::cout << "   view: " << this << " " << text_ << std::endl;
}

logging_string(logging_string&& other) {
std::cout << "   move: " << this << " <= " << &other << " new <= "
<< other.text_ << std::endl;
text_ = std::move(other.text_);
}

~logging_string(){
std::cout << "   destruct: " << this << " " << text_ << std::endl;
}

logging_string& operator=(logging_string&& other){
std::cout << "move-assign: " << this << " <= " << &other << " " <<
text_ << " <= " << other.text_ << std::endl;
text_ = std::move(other.text_);
return *this;
}

private:
std::string text_;
};

struct wrapper{
// wrapper() = default;
// wrapper(std::string_view text) :filename(text) {}
// wrapper(wrapper&&) = default;
// wrapper& operator=(wrapper&&) = default;
// ~wrapper() = default;

logging_string filename;
};


struct generator{
struct promise_type;
using handle_type = std::coroutine_handle;

struct promise_type{
wrapper value{"default"sv};
std::exception_ptr exception;

generator get_return_object(){
return handle_type::from_promise(*this);
}

std::suspend_always initial_suspend(){
return {};
}

std::suspend_always final_suspend()noexcept{
return {};
}

void unhandled_exception(){ exception = std::current_exception(); }

std::suspend_always yield_value(wrapper&& new_value){
value = std::move(new_value);
return {};
}

void return_void(){}
};

generator(handle_type h)
: handle_(h)
{}

generator(generator&& other)
: handle_(other.handle_)
{
other.handle_ = nullptr;
}

generator& operator=(generator&& other){
handle_ = other.handle_;
other.handle_ = nullptr;
return *this;
}

~generator(){
if(handle_){
handle_.destroy();
}
}

explicit operator bool(){
fill();
return !handle_.done();
}

wrapper operator()(){
fill();
full_ = false;
return std::move(handle_.promise().value);
}

private:
handle_type handle_;
bool full_ = false;

void fill(){
if(!full_){
handle_();
if(handle_.promise().exception){
std::rethrow_exception(handle_.promise().exception);
}
full_ = true;
}
}
};


static generator generate(){
co_yield {"generate"sv};
}


int main(){
auto gen = generate();
(void)static_cast(gen);
}
```

Crashes with output:

```text
   view: 0xea2ec0 default
   view: 0xea2f20 generate
move-assign: 0xea2ec0 <= 0xea2f00 default <= generate
   destruct: 0xea2f00 
   destruct: 0xea2f20 generate
   destruct: 0xea2ec0 generate
free(): invalid size
Aborted (core dumped)
```

The move-assign looks very strange because the address of the "other" object is
not one of the previously created objects!

If you uncomment the wrapper functions in the minimal example, the program runs
fine and generates the expected output:

```text
   view: 0x129aec0 default
   view: 0x129af00 generate
move-assign: 0x129aec0 <= 0x129af00 default <= generate
   destruct: 0x129af00 
   destruct: 0x129aec0 generate
```

Also the move-assign looks as expected now.

You get the same valid output (regardless of whether the wrapper functions are
defined or not) when compiling with trunk of clang and msvc. (Tested via
Compiler Explorer.)

I also tested clang trunk with "-stdlib=libstdc++" which also works fine. So
the bug is probably in the g++ compiler, not in the libstdc++ library.

[Bug c++/104872] Memory corruption in Coroutine with POD type

2022-03-11 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104872

--- Comment #1 from Benjamin Buch  ---
More minimal version:

https://godbolt.org/z/aEv13e38a

```cpp
#include 
#include 
#include 


using namespace std::literals;


class logging_string{
public:
logging_string(std::string_view text) :text_(text) {
std::cout << "   view: " << this << " " << text_ << std::endl;
}

logging_string(logging_string&& other) {
std::cout << "   move: " << this << " <= " << &other << " new <= "
<< other.text_ << std::endl;
text_ = std::move(other.text_);
}

~logging_string(){
std::cout << "   destruct: " << this << " " << text_ << std::endl;
}

logging_string& operator=(logging_string&& other){
std::cout << "move-assign: " << this << " <= " << &other << " " <<
text_ << " <= " << other.text_ << std::endl;
text_ = std::move(other.text_);
return *this;
}

private:
std::string text_;
};

struct wrapper{
// wrapper() = default;
// wrapper(std::string_view text) :filename(text) {}
// wrapper(wrapper&&) = default;
// wrapper& operator=(wrapper&&) = default;
// ~wrapper() = default;

logging_string filename;
};


struct generator{
struct promise_type;
using handle_type = std::coroutine_handle;

struct promise_type{
wrapper value{"default"sv};

generator get_return_object(){
return handle_type::from_promise(*this);
}

std::suspend_always initial_suspend(){
return {};
}

std::suspend_always final_suspend()noexcept{
return {};
}

void unhandled_exception(){}

std::suspend_always yield_value(wrapper&& new_value){
value = std::move(new_value);
return {};
}
};

generator(handle_type h)
: handle(h)
{}

~generator(){
handle.destroy();
}

handle_type handle;
};


static generator generate(){
co_yield {"generate"sv};
}


int main(){
auto gen = generate();
gen.handle();
}
```

[Bug c++/104872] Memory corruption in Coroutine with POD type

2022-03-11 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104872

--- Comment #2 from Benjamin Buch  ---
To workaround it is enough define the wrapper constructor to build a string.

```cpp
wrapper(std::string text): filename(std::move(text)) {}
```

https://godbolt.org/z/9za7hfjs8

```cpp
#include 
#include 


using namespace std::literals;


class logging_string{
public:
logging_string(std::string text): text_(std::move(text)) {
std::cout << "   view: " << this << " " << text_ << std::endl;
}

logging_string(logging_string&& other) {
std::cout << "   move: " << this << " <= " << &other << " new <= "
<< other.text_ << std::endl;
text_ = std::move(other.text_);
}

~logging_string(){
std::cout << "   destruct: " << this << " " << text_ << std::endl;
}

logging_string& operator=(logging_string&& other){
std::cout << "move-assign: " << this << " <= " << &other << " " <<
text_ << " <= " << other.text_ << std::endl;
text_ = std::move(other.text_);
return *this;
}

private:
std::string text_;
};

struct wrapper{
// wrapper(std::string text): filename(std::move(text)) {}

logging_string filename;
};


struct generator{
struct promise_type;
using handle_type = std::coroutine_handle;

struct promise_type{
wrapper value{"default"s};

generator get_return_object(){
return handle_type::from_promise(*this);
}

std::suspend_always initial_suspend(){
return {};
}

std::suspend_always final_suspend()noexcept{
return {};
}

void unhandled_exception(){}

std::suspend_always yield_value(wrapper&& new_value){
value = std::move(new_value);
return {};
}
};

generator(handle_type h)
: handle(h)
{}

~generator(){
handle.destroy();
}

handle_type handle;
};


static generator generate(){
co_yield {"generate"s};
}


int main(){
auto gen = generate();
gen.handle();
}
```

[Bug c++/104872] Memory corruption in Coroutine with POD type

2022-11-02 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104872

--- Comment #4 from Benjamin Buch  ---
Can you please increase the priority? P3 seems too low for the wrong code. With
ICE I could understand that, but here the code seems to be compiled
successfully and then crashes when running the program. This is security
relevant!

Bug is still present in GCC 12.2 and current trunk.

[Bug c++/104872] Memory corruption in Coroutine with POD type

2022-06-21 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104872

--- Comment #3 from Benjamin Buch  ---
Bug is still present in GCC 12.1 and current trunk.

[Bug c++/114076] New: list-initialization with assignment expression triggers wrong template instanciation

2024-02-23 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114076

Bug ID: 114076
   Summary: list-initialization with assignment expression
triggers wrong template instanciation
   Product: gcc
   Version: 13.2.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: benni.buch at gmail dot com
  Target Milestone: ---

I come from this Bug Report to Visual C++:

- https://github.com/microsoft/STL/issues/4417

I think that GCC has a similar bug here.

```cpp
template 
struct holder {
holder() = default;

constexpr ~holder() {
static_assert(sizeof(T) || true);
}
};

struct Incomplete;

struct Class {
Class();
~Class();

holder a{};
holder b = {};
//holder c = holder{};
};

int main() {
[[maybe_unused]] Class v;
}
```

- https://godbolt.org/z/ds3WYK55f

Cases a and b can both be compiled with Clang. GCC rejects b, which is wrong in
my opinion.

If I understand it correctly, then cases a and b should result in absolutely
identical initializations. The destructor of holder should not be instantiated.

In case c, the rejection seems to me to be correct, since here the temporary
value must be destroyed by a destructor call.

Among other things, this error affects unique_ptr.

[Bug c++/114076] list-initialization with assignment expression triggers wrong template instanciation

2024-03-05 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114076

--- Comment #3 from Benjamin Buch  ---
I [created an overview](https://stackoverflow.com/a/78101462/4821621) with all
cases that currently work on StackOverflow. I think that all these cases should
be valid. For a properly formated version with links to Compiler Explorer
please use my stackoverflow post. Here is the relevant part

GitHub user
[frederick-vs-ja](https://github.com/microsoft/STL/issues/4417#issuecomment-1960976417)
has reduced the problem in the MS STL Bug Report to a minimal example.

```cpp
template 
struct Holder {
Holder() = default;
constexpr ~Holder() { static_assert(sizeof(T) || true); }
};

struct Incomplete;

struct Class {
Class();
~Class();

Holder v{};
};

int main() { [[maybe_unused]] Class v; }
```

I have extended this example with some variants and tested the compilers with
them. Syntactically, there are 7 ways to call the default constructor of
Holder:

- `Holder a;` default-initialization
- `Holder b{};` direct-list-initialization
- `Holder c = {};` copy-list-initialization
- `Holder d = Holder();` copy-direct-initialization
- `Holder e = {Holder()};` copy-list-initialization
- `Holder f = Holder{};` copy-direct-initialization
- `Holder g = {Holder{}};` copy-list-initialization

Then we can change `Holder` in two ways. (Thanks to Github user
[fsb4000](https://github.com/microsoft/STL/issues/4417#issuecomment-1959604006)
in the MS STL bug report!)

First we can remove the explicit constructor definition, so the compiler
generates this constructor implicitly:

```cpp
template 
struct Holder {
constexpr ~Holder() { static_assert(sizeof(T) || true); }
};
```

Second we can remove the constexpr from the destructor:

```cpp
template 
struct Holder {
Holder() = default;
~Holder() { static_assert(sizeof(T) || true); }
};
```

A third relevant change can be applied to `B`. (Thanks to [Jiang
An](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114076#c1) in the GCC bug
report!) We can make it a template, by giving it a default template argument:

```cpp
template 
struct Class {
Class();
~Class();

Holder v{};
};
```

So we have 8 code examples with 7 kinds of initialization. I created a table
with what works with which compiler. I tested with MSVC 19.38, GCC 13.2 and
clang 17.0.1. Each table cell links to the live code, where the trunk versions
of the compilers are used additionally.

- Case 1: *Implicit `Holder` ctor*, *non-`constexpr` dtor*, *non-template
`Class`*
- Case 2: Explicit `Holder` ctor, *non-`constexpr` dtor*, *non-template
`Class`*
- Case 3: *Implicit `Holder` ctor*, `constexpr` dtor, *non-template `Class`*
- Case 4: Explicit `Holder` ctor, `constexpr` dtor, *non-template `Class`*
- Case 5: *Implicit `Holder` ctor*, *non-`constexpr` dtor*, template `Class`
- Case 6: Explicit `Holder` ctor, *non-`constexpr` dtor*, template `Class`
- Case 7: *Implicit `Holder` ctor*, `constexpr` dtor, template `Class`
- Case 8: Explicit `Holder` ctor, `constexpr` dtor, template `Class`

The order is: ✅/❌ MSVC ; ✅/❌ GCC ; ✅/❌ clang

||Case a|Case b|Case c|Case d|Case e|Case f|Case g|
|-|-|-|-|-|-|-|-|
|Case 1:|✅✅✅|✅✅✅|✅✅✅|✅❌✅|✅❌✅|✅❌✅|✅❌✅|
|Case 2:|✅✅✅|✅✅✅|✅❌✅|✅❌✅|✅❌✅|✅❌✅|✅❌✅|
|Case 3:|✅✅✅|❌✅✅|❌✅✅|❌❌❌|❌❌❌|❌❌❌|❌❌❌|
|Case 4:|✅✅✅|❌✅✅|❌❌✅|❌❌❌|❌❌❌|❌❌❌|❌❌❌|
|Case 5:|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|
|Case 6:|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|✅✅✅|
|Case 7:|✅✅✅|✅✅✅|✅✅✅|✅✅❌|✅✅❌|✅✅❌|✅✅❌|
|Case 8:|✅✅✅|✅✅✅|✅✅✅|✅✅❌|✅✅❌|✅✅❌|✅✅❌|

Cases d, e, f and g can apparently be considered as one case.

[Bug c++/104850] Instantiating a destructor for a template class too early, before the calling destructor is seen - rejects valid code

2024-03-05 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104850

Benjamin Buch  changed:

   What|Removed |Added

 CC||benni.buch at gmail dot com

--- Comment #6 from Benjamin Buch  ---
I think this is a sub-case of Bug 104850

[Bug c++/104850] Instantiating a destructor for a template class too early, before the calling destructor is seen - rejects valid code

2024-03-05 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104850

--- Comment #7 from Benjamin Buch  ---
Sorry wrong number; Bug 114076

[Bug c++/114564] New: Accessing template Base via template Derived fails

2024-04-02 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114564

Bug ID: 114564
   Summary: Accessing template Base via template Derived fails
   Product: gcc
   Version: 13.2.1
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: benni.buch at gmail dot com
  Target Milestone: ---

```cpp
template 
struct Base {};

template 
struct Derived: Base {
Derived(Derived::Base obj);
};
```

Accessing Base via Derived works with clang and MSVC, but not with GCC.

Live:

- https://godbolt.org/z/Wv3EK3e71

[Bug c++/114076] list-initialization with assignment expression triggers wrong template instanciation

2024-08-14 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=114076

--- Comment #4 from Benjamin Buch  ---
I just tried the Compiler Explorer links from my overview and saw that there is
no change in the trunk for GCC.

https://stackoverflow.com/a/78101462/4821621

The fact that all three major compilers behave differently here is a problem
for portability.

Would be nice if someone could take a look at this ;-)

[Bug libstdc++/109299] New: wrong warning on std::wstring with -O2 -std=c++20 -D_FORTIFY_SOURCE=2

2023-03-27 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109299

Bug ID: 109299
   Summary: wrong warning on std::wstring with -O2 -std=c++20
-D_FORTIFY_SOURCE=2
   Product: gcc
   Version: 12.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: libstdc++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: benni.buch at gmail dot com
  Target Milestone: ---

I run into this by using googletest with CMake in release mode.

```
#include 

static std::wstring foo(std::wstring text = {}) {
text.resize(42);
return text;
}

int main() {
foo();
}
```

```
$ g++ -O2 -std=c++20 -D_FORTIFY_SOURCE=2 main.cpp 
In file included from /usr/include/features.h:486,
 from
/usr/include/x86_64-linux-gnu/c++/12/bits/os_defines.h:39,
 from
/usr/include/x86_64-linux-gnu/c++/12/bits/c++config.h:655,
 from /usr/include/c++/12/string:38,
 from main.cpp:1:
In function ‘wchar_t* wmemcpy(wchar_t*, const wchar_t*, size_t)’,
inlined from ‘static constexpr std::char_traits::char_type*
std::char_traits::copy(char_type*, const char_type*, std::size_t)’ at
/usr/include/c++/12/bits/char_traits.h:558:16,
inlined from ‘constexpr std::__cxx11::basic_string<_CharT, _Traits,
_Alloc>::basic_string(std::__cxx11::basic_string<_CharT, _Traits, _Alloc>&&)
[with _CharT = wchar_t; _Traits = std::char_traits; _Alloc =
std::allocator]’ at /usr/include/c++/12/bits/basic_string.h:675:23,
inlined from ‘std::wstring foo(std::wstring)’ at main.cpp:5:12,
inlined from ‘int main()’ at main.cpp:9:8:
/usr/include/x86_64-linux-gnu/bits/wchar2.h:42:10: warning: call to
‘__wmemcpy_chk_warn’ declared with attribute warning: wmemcpy called with
length bigger than size of destination buffer [-Wattribute-warning]
   42 |   return __glibc_fortify_n (wmemcpy, __n, sizeof (wchar_t),
  |  ^
```

On stackoverflow.com you can already find a few discussion about it:

- https://stackoverflow.com/questions/75689057

user17732522 reduced it to:

```
#include 

wchar_t* _M_dataplus;
int _M_string_length;

wchar_t _M_local_buf[4];
wchar_t _M_local_buf2[4];

void g();

void f() {
g();
_M_string_length = 4;
if (_M_dataplus == _M_local_buf)
wmemcpy(_M_local_buf2, _M_local_buf, _M_string_length + 1);
}
```

```
$ g++ -O1 -D_FORTIFY_SOURCE=2 -c main.cpp 
In file included from /usr/include/features.h:486,
 from
/usr/include/x86_64-linux-gnu/bits/libc-header-start.h:33,
 from /usr/include/wchar.h:27,
 from main.cpp:1:
In function ‘wchar_t* wmemcpy(wchar_t*, const wchar_t*, size_t)’,
inlined from ‘void f()’ at main.cpp:15:16:
/usr/include/x86_64-linux-gnu/bits/wchar2.h:42:10: warning: call to
‘__wmemcpy_chk_warn’ declared with attribute warning: wmemcpy called with
length bigger than size of destination buffer [-Wattribute-warning]
   42 |   return __glibc_fortify_n (wmemcpy, __n, sizeof (wchar_t),
  |  ^
```

[Bug tree-optimization/109299] wrong warning on std::wstring with -O2 -std=c++20 -D_FORTIFY_SOURCE=2

2023-03-27 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109299

Benjamin Buch  changed:

   What|Removed |Added

Version|12.0|12.1.0

--- Comment #3 from Benjamin Buch  ---
You are right, I use GCC 12.1.0, sorry!

I also ran reproduce it via godbolt.org starting from version 12.1 until
current trunk.

https://godbolt.org/z/Y9frhGr86

[Bug libstdc++/104928] std::counting_semaphore on Linux can sleep forever

2025-01-04 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928

Benjamin Buch  changed:

   What|Removed |Added

 CC||benni.buch at gmail dot com

--- Comment #8 from Benjamin Buch  ---
I came here from https://www.reddit.com/r/cpp/comments/1htews2

Shouldn't this bug have a P1 priority? Wrong code is critical after all.

[Bug libstdc++/104928] std::counting_semaphore on Linux can sleep forever

2025-01-04 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928

--- Comment #10 from Benjamin Buch  ---
Okay, I understand this.

https://gcc.gnu.org/onlinedocs/libstdc++/manual/status.html#status.iso.2020

defines P1135R6 is implemented since libstdc++ 11.1 and the feature test macro
__cpp_lib_semaphore >= 201907L is defined accordingly while the implementation
is known to produce wrong code for nearly 2 years and three major releases now.
It brings us into a situation again, where users have to detect compiler and
compiler version to check whether they can use std::counting_semaphore instead
of just checking the feature test macro.

In my option this is a very bad and hard to acceptable situation, even in an
experimental mode. Especially since C++20 already has a wide adoption these
days.

So I ask you to undefine the feature test macro and set the implementation
state to partial.

Also I propose to introduce -std=c++XXpreview flags for all (new) experimental
language modes instead of -std=c++XX which misleadingly indicates that the mode
is ready to use. The documentation might name the modes as experimental, but I
think its much more user-friendly to make the flag itself expressive.

(MSVC also starts doing this with the upcoming release 2022 17.13 for C++23.
There will be a c++23preview mode.)

I understand that this might break existing code. Nevertheless in my opinion
its better to loudly fail at compile time that silently doing the wrong thing
at runtime. We all know that software contains bug. That's normal. To actively
inform the users is important and user-friendly. Sometimes even more important
than fixing the bug itself. ;-)

[Bug libstdc++/104928] std::counting_semaphore on Linux can sleep forever

2025-01-04 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928

--- Comment #12 from Benjamin Buch  ---
The point is that the great majority of users do not read the full
documentation to find the section that mentions the experimental character of
the latest C++ modes. To be explicit about this, by just using a better name
would be very helpful.

Of course user will continue to use those, but they would know, that the modes
are experimental and have not the same priority as non-experimental modes.

Intuitive interfaces are better then large documentation that only a few users
read and also only if they have a problem.

[Bug libstdc++/104928] std::counting_semaphore on Linux can sleep forever

2025-01-05 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928

--- Comment #13 from Benjamin Buch  ---
According to the CLI interface I opened a backwards compatible feature request:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118301

[Bug c++/118301] New: Feature request: CLI parament std with explicit experimental values

2025-01-05 Thread benni.buch at gmail dot com via Gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=118301

Bug ID: 118301
   Summary: Feature request: CLI parament std with explicit
experimental values
   Product: gcc
   Version: 15.0
Status: UNCONFIRMED
  Severity: normal
  Priority: P3
 Component: c++
  Assignee: unassigned at gcc dot gnu.org
  Reporter: benni.buch at gmail dot com
  Target Milestone: ---

I'm coming from:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=104928

Currently you can set the C++ standard via -std=c++17/20/23... While C++17 is
officially supported, C++20 and higher are still experimental which means even
serious bugs like wrong code generation are not handled with the same priority
as similar bugs in C++17 and lower. 

Even professional users are already using C++20 and higher in production
because they are not aware that these modes are still experimental and don't
get the same level of support. You can blame the users for not reading the
documentation. 

On the other hand you can argue, that GCCs (and clangs) CLI interface is a bit
misleading, because stable and testing modes look exactly the same. MSVC will
introduce a c++23preview argument with 2022 17.13. I really like this idea,
because it makes the experimental character very clear to the user.

Changing GCCs current interface would be a bad idea, because it would break a
lot of existing code.

Therefore I propose to introduce c++20/23...preview arguments that have the
same effect as the current non-preview arguments. The current non-preview
arguments should continue to work, but print a warning to use the preview
arguments instead.