public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug libstdc++/113007] New: `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not
@ 2023-12-13 21:11 paul.skeptic at yandex dot ru
2023-12-13 21:15 ` [Bug libstdc++/113007] " pinskia at gcc dot gnu.org
` (7 more replies)
0 siblings, 8 replies; 9+ messages in thread
From: paul.skeptic at yandex dot ru @ 2023-12-13 21:11 UTC (permalink / raw)
To: gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113007
Bug ID: 113007
Summary: `std::variant` converting constructor and `operator=`
compile while the C++ Standard says they must not
Product: gcc
Version: 13.2.1
Status: UNCONFIRMED
Severity: normal
Priority: P3
Component: libstdc++
Assignee: unassigned at gcc dot gnu.org
Reporter: paul.skeptic at yandex dot ru
Target Milestone: ---
According to [variant.ctor] (https://eel.is/c++draft/variant.ctor, also see
https://en.cppreference.com/w/cpp/utility/variant/variant) converting
constructor
template<class T>
constexpr variant(T&& t) noexcept(/*...*/);
determines the alternative to construct as if by selecting an overload for
`F(std::forward<T>(t))` from an overload set `F(T_j)` where `T_j` are types
of the alternatives,
e.g. for `std::variant<std::monostate, bool, int64_t, double>` the overload set
would be
void F(std::monostate) {}
void F(bool) {}
void F(int64_t) {}
void F(double) {}
In violation of the standard this code compiles with GCC and libstdc++
(https://godbolt.org/z/fd8xa1bqM):
std::variant<std::monostate, bool, int64_t, double> v = 42;
while overload resolution of `F()` fails:
F(42); // error: ambiguous confused_john_travolta.gif
Compiler output:
<source>: In function 'int main()':
<source>:14:4: error: call of overloaded 'F(int)' is ambiguous
14 | F(42);
| ~^~~~
<source>:6:6: note: candidate: 'void F(bool)'
6 | void F(bool) {}
| ^
<source>:7:6: note: candidate: 'void F(int64_t)'
7 | void F(int64_t) {}
| ^
<source>:8:6: note: candidate: 'void F(double)'
8 | void F(double) {}
| ^
Also the same applies to converting assignment operator
template<class T>
constexpr variant& operator=(T&& t) noexcept(/*...*/);
see [variant.assign] (https://eel.is/c++draft/variant.assign, also
https://en.cppreference.com/w/cpp/utility/variant/operator%3D).
Among libc++, libstdc++ and MSVC standard libraries only MSVC standard library
gets it according to the standard so you can look at it for reference.
See corresponding bug for libc++:
https://github.com/llvm/llvm-project/issues/75323
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Bug libstdc++/113007] `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not
2023-12-13 21:11 [Bug libstdc++/113007] New: `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not paul.skeptic at yandex dot ru
@ 2023-12-13 21:15 ` pinskia at gcc dot gnu.org
2023-12-14 1:03 ` redi at gcc dot gnu.org
` (6 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: pinskia at gcc dot gnu.org @ 2023-12-13 21:15 UTC (permalink / raw)
To: gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113007
--- Comment #1 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
Created attachment 56874
--> https://gcc.gnu.org/bugzilla/attachment.cgi?id=56874&action=edit
Full testcase
Please next time attach the full testcase (or place it inline) instead of just
linking to godbolt.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Bug libstdc++/113007] `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not
2023-12-13 21:11 [Bug libstdc++/113007] New: `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not paul.skeptic at yandex dot ru
2023-12-13 21:15 ` [Bug libstdc++/113007] " pinskia at gcc dot gnu.org
@ 2023-12-14 1:03 ` redi at gcc dot gnu.org
2023-12-14 1:06 ` redi at gcc dot gnu.org
` (5 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: redi at gcc dot gnu.org @ 2023-12-14 1:03 UTC (permalink / raw)
To: gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113007
--- Comment #2 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Pavel Novikov from comment #0)
> e.g. for `std::variant<std::monostate, bool, int64_t, double>` the overload
> set would be
>
> void F(std::monostate) {}
> void F(bool) {}
> void F(int64_t) {}
> void F(double) {}
No, because it says:
"build an imaginary function FUN(Ti) for each alternative type Ti for which Ti
x[] = {std::forward<T>(t)}; is well-formed for some invented variable x."
That array initializer would be ill-formed for all except int64_t.
std::monostate just can't be initialized from an int, and for double and bool
it's a narrowing conversion (because of the braces).
I think libstdc++ is correct.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Bug libstdc++/113007] `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not
2023-12-13 21:11 [Bug libstdc++/113007] New: `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not paul.skeptic at yandex dot ru
2023-12-13 21:15 ` [Bug libstdc++/113007] " pinskia at gcc dot gnu.org
2023-12-14 1:03 ` redi at gcc dot gnu.org
@ 2023-12-14 1:06 ` redi at gcc dot gnu.org
2023-12-14 10:58 ` paul.skeptic at yandex dot ru
` (4 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: redi at gcc dot gnu.org @ 2023-12-14 1:06 UTC (permalink / raw)
To: gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113007
Jonathan Wakely <redi at gcc dot gnu.org> changed:
What |Removed |Added
----------------------------------------------------------------------------
Status|UNCONFIRMED |RESOLVED
Resolution|--- |INVALID
--- Comment #3 from Jonathan Wakely <redi at gcc dot gnu.org> ---
#include <variant>
int main()
{
int i = 42;
std::monostate x_0[] = { i };
bool x_1[] = { i };
long long x_2[] = { i };
double x_3[] = { i };
}
var.cc: In function ‘int main()’:
var.cc:6:30: error: initializer for ‘std::monostate’ must be brace-enclosed
6 | std::monostate x_0[] = { i };
| ^
var.cc:7:18: warning: narrowing conversion of ‘i’ from ‘int’ to ‘bool’
[-Wnarrowing]
7 | bool x_1[] = { i };
| ^
var.cc:9:20: warning: narrowing conversion of ‘i’ from ‘int’ to ‘double’
[-Wnarrowing]
9 | double x_3[] = { i };
| ^
Narrowing conversions are hard errors in SFINAE contexts, even though they're
just a warning in other contexts.
Only x_2 is well-formed, and so the overload set is just F(int64_t).
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Bug libstdc++/113007] `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not
2023-12-13 21:11 [Bug libstdc++/113007] New: `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not paul.skeptic at yandex dot ru
` (2 preceding siblings ...)
2023-12-14 1:06 ` redi at gcc dot gnu.org
@ 2023-12-14 10:58 ` paul.skeptic at yandex dot ru
2023-12-14 14:18 ` redi at gcc dot gnu.org
` (3 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: paul.skeptic at yandex dot ru @ 2023-12-14 10:58 UTC (permalink / raw)
To: gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113007
--- Comment #4 from Pavel Novikov <paul.skeptic at yandex dot ru> ---
Interesting. Thank you for explanation.
Indeed the standard says all over that narrowing conversion in initialization
is prohibited, though this code compiles:
int i = 42;
bool b[] = {i}; // narrowing
int64_t i64[] = {i};
double d[] = {i}; // narrowing*
(*putting aside that any `int` can be _exactly_ represented by a double
precision floating point value)
As an approximation for `T x[] = {t}` requirement I came up with this code:
template<typename T, typename U>
std::enable_if_t<sizeof(decltype(T{std::declval<U>()}))>
test() {}
and indeed it fails to compile when narrwoing conversion is required, i.e.
test<bool, int>(); // error: narrowing
test<int64_t, int>();
test<double, int>(); // error: narrowing
So turning
> an imaginary function FUN(Ti) for each alternative type Ti for which Ti x[] = {std::forward<T>(t)}; is well-formed for some invented variable x.
into code using the approximation above we have the following code
template<typename T, typename U>
std::enable_if_t<sizeof(decltype(T{std::declval<U>()}))>
F(U) {}
F<bool>(42); // error: deduction/substitution failed due to narrowing
F<int64_t>(42);
F<double>(42); // error: deduction/substitution failed due to narrowing
And if we consider `F<T_j>()` functions as an overload set, then indeed only
`F<int64_t>()` is well formed considering (non)narrowing requirement and is
ultimately selected.
(See https://godbolt.org/z/sxof45Eeh)
Did I get it right?
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Bug libstdc++/113007] `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not
2023-12-13 21:11 [Bug libstdc++/113007] New: `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not paul.skeptic at yandex dot ru
` (3 preceding siblings ...)
2023-12-14 10:58 ` paul.skeptic at yandex dot ru
@ 2023-12-14 14:18 ` redi at gcc dot gnu.org
2023-12-14 14:20 ` redi at gcc dot gnu.org
` (2 subsequent siblings)
7 siblings, 0 replies; 9+ messages in thread
From: redi at gcc dot gnu.org @ 2023-12-14 14:18 UTC (permalink / raw)
To: gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113007
--- Comment #5 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Pavel Novikov from comment #4)
> Indeed the standard says all over that narrowing conversion in
> initialization is prohibited, though this code compiles:
>
> int i = 42;
> bool b[] = {i}; // narrowing
> int64_t i64[] = {i};
> double d[] = {i}; // narrowing*
They compile *with a diagnostic* (a warning, specifically). That is what the
standard requires for ill-formed code, and those narrowing conversions are
ill-formed. If you want them to be errors not warnings, you can use
-Werror=narrowing. In a SFINAE context, they result in substitution failure,
because "warn and continue" is not an option for substitution failures.
> (*putting aside that any `int` can be _exactly_ represented by a double
> precision floating point value)
Assuming 32-bit int and 64-bit double, yes. That isn't guaranteed by the
standard though: int could have 64 bits, for example. To avoid unportable code,
it's considered to be narrowing "except where the source is a constant
expression and the actual value after conversion will fit into the target type
and will produce the original value when converted back to the original type".
So double d{42} is not a narrowing conversion, but double d{i} is.
> As an approximation for `T x[] = {t}` requirement I came up with this code:
>
> template<typename T, typename U>
> std::enable_if_t<sizeof(decltype(T{std::declval<U>()}))>
> test() {}
>
> and indeed it fails to compile when narrwoing conversion is required, i.e.
>
> test<bool, int>(); // error: narrowing
> test<int64_t, int>();
> test<double, int>(); // error: narrowing
>
> So turning
>
> > an imaginary function FUN(Ti) for each alternative type Ti for which Ti x[] = {std::forward<T>(t)}; is well-formed for some invented variable x.
>
> into code using the approximation above we have the following code
>
> template<typename T, typename U>
> std::enable_if_t<sizeof(decltype(T{std::declval<U>()}))>
> F(U) {}
>
> F<bool>(42); // error: deduction/substitution failed due to narrowing
> F<int64_t>(42);
> F<double>(42); // error: deduction/substitution failed due to narrowing
>
> And if we consider `F<T_j>()` functions as an overload set, then indeed only
> `F<int64_t>()` is well formed considering (non)narrowing requirement and is
> ultimately selected.
> (See https://godbolt.org/z/sxof45Eeh)
>
> Did I get it right?
Yes. Libstdc++ uses something similar to your enable_if, but using void_t
instead:
template<typename _Ti> struct _Arr { _Ti _M_x[1]; };
template<size_t _Ind, typename _Tp, typename _Ti>
struct _Build_FUN<_Ind, _Tp, _Ti,
void_t<decltype(_Arr<_Ti>{{std::declval<_Tp>()}})>>
The _Arr struct is needed to ensure that we're trying to convert to T{1} not
just T, which is what the standard wants.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Bug libstdc++/113007] `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not
2023-12-13 21:11 [Bug libstdc++/113007] New: `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not paul.skeptic at yandex dot ru
` (4 preceding siblings ...)
2023-12-14 14:18 ` redi at gcc dot gnu.org
@ 2023-12-14 14:20 ` redi at gcc dot gnu.org
2023-12-14 15:44 ` paul.skeptic at yandex dot ru
2024-02-19 10:13 ` de34 at live dot cn
7 siblings, 0 replies; 9+ messages in thread
From: redi at gcc dot gnu.org @ 2023-12-14 14:20 UTC (permalink / raw)
To: gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113007
--- Comment #6 from Jonathan Wakely <redi at gcc dot gnu.org> ---
(In reply to Jonathan Wakely from comment #5)
> The _Arr struct is needed to ensure that we're trying to convert to T{1} not
Oops, I mean T[1] not T{1} of course!
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Bug libstdc++/113007] `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not
2023-12-13 21:11 [Bug libstdc++/113007] New: `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not paul.skeptic at yandex dot ru
` (5 preceding siblings ...)
2023-12-14 14:20 ` redi at gcc dot gnu.org
@ 2023-12-14 15:44 ` paul.skeptic at yandex dot ru
2024-02-19 10:13 ` de34 at live dot cn
7 siblings, 0 replies; 9+ messages in thread
From: paul.skeptic at yandex dot ru @ 2023-12-14 15:44 UTC (permalink / raw)
To: gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113007
--- Comment #7 from Pavel Novikov <paul.skeptic at yandex dot ru> ---
(In reply to Jonathan Wakely from comment #5&6)
Got it.
Thank you for taking the time to expansively explain what I left out, and
confirming my understanding.
Now it's time to file a bug in MSVC's standard library.
^ permalink raw reply [flat|nested] 9+ messages in thread
* [Bug libstdc++/113007] `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not
2023-12-13 21:11 [Bug libstdc++/113007] New: `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not paul.skeptic at yandex dot ru
` (6 preceding siblings ...)
2023-12-14 15:44 ` paul.skeptic at yandex dot ru
@ 2024-02-19 10:13 ` de34 at live dot cn
7 siblings, 0 replies; 9+ messages in thread
From: de34 at live dot cn @ 2024-02-19 10:13 UTC (permalink / raw)
To: gcc-bugs
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=113007
Jiang An <de34 at live dot cn> changed:
What |Removed |Added
----------------------------------------------------------------------------
CC| |de34 at live dot cn
--- Comment #8 from Jiang An <de34 at live dot cn> ---
(In reply to Pavel Novikov from comment #7)
> Now it's time to file a bug in MSVC's standard library.
This was by design. MSVC STL intendedly only enabled changes in P0608R3 since
C++20 (https://github.com/microsoft/STL/pull/1629#issuecomment-778895630).
But given the author of P0608R3 applied the changes to libc++ in C++17 mode, I
think it makes more sense to treat it as a DR.
I've opened this issue:
https://github.com/microsoft/STL/issues/4412
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2024-02-19 10:13 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-12-13 21:11 [Bug libstdc++/113007] New: `std::variant` converting constructor and `operator=` compile while the C++ Standard says they must not paul.skeptic at yandex dot ru
2023-12-13 21:15 ` [Bug libstdc++/113007] " pinskia at gcc dot gnu.org
2023-12-14 1:03 ` redi at gcc dot gnu.org
2023-12-14 1:06 ` redi at gcc dot gnu.org
2023-12-14 10:58 ` paul.skeptic at yandex dot ru
2023-12-14 14:18 ` redi at gcc dot gnu.org
2023-12-14 14:20 ` redi at gcc dot gnu.org
2023-12-14 15:44 ` paul.skeptic at yandex dot ru
2024-02-19 10:13 ` de34 at live dot cn
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).