From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: by sourceware.org (Postfix, from userid 48) id 428F33858C62; Fri, 4 Aug 2023 18:57:13 +0000 (GMT) DKIM-Filter: OpenDKIM Filter v2.11.0 sourceware.org 428F33858C62 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gcc.gnu.org; s=default; t=1691175433; bh=Gyp1DsamXuMLYP67F4A6O20S1Q1DyplobbB7dyvnjNI=; h=From:To:Subject:Date:From; b=b54Ncm/Rs/8sLOkQUtrwLV6iYmPiEfiVfGRcHq7MwTHiQkrodSCtjJlj6Ka87mWlV FHAJM8EjRJdWB9QV48b8omZP02+WYlUKvNX0xKb83Vjy3p6TyHKdiwiCDCiTybz97z H3z1tY0iRmjcyDm/38QlTRDLlsTLZhqMvQSiT1Hg= From: "danakj at orodu dot net" To: gcc-bugs@gcc.gnu.org Subject: [Bug c++/110905] New: GCC rejects constexpr code that may re-initialize union member Date: Fri, 04 Aug 2023 18:57:12 +0000 X-Bugzilla-Reason: CC X-Bugzilla-Type: new X-Bugzilla-Watch-Reason: None X-Bugzilla-Product: gcc X-Bugzilla-Component: c++ X-Bugzilla-Version: 13.2.1 X-Bugzilla-Keywords: X-Bugzilla-Severity: normal X-Bugzilla-Who: danakj at orodu dot net X-Bugzilla-Status: UNCONFIRMED X-Bugzilla-Resolution: X-Bugzilla-Priority: P3 X-Bugzilla-Assigned-To: unassigned at gcc dot gnu.org X-Bugzilla-Target-Milestone: --- X-Bugzilla-Flags: X-Bugzilla-Changed-Fields: bug_id short_desc product version bug_status bug_severity priority component assigned_to reporter target_milestone Message-ID: Content-Type: text/plain; charset="UTF-8" Content-Transfer-Encoding: quoted-printable X-Bugzilla-URL: http://gcc.gnu.org/bugzilla/ Auto-Submitted: auto-generated MIME-Version: 1.0 List-Id: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=3D110905 Bug ID: 110905 Summary: GCC rejects constexpr code that may re-initialize union member Product: gcc Version: 13.2.1 Status: UNCONFIRMED Severity: normal Priority: P3 Component: c++ Assignee: unassigned at gcc dot gnu.org Reporter: danakj at orodu dot net Target Milestone: --- Godbolt: https://gcc.godbolt.org/z/v5anxqnP1 This repro contains a std::optional (which has a union) and it sets the uni= on in a loop. Doing so causes GCC to reject the code as not being a constant expression. The error I was getting in my project was far more descriptive, with it trying to call the deleted constructor of the union. error: use of deleted function =E2=80=98sus::option::__private::Storage, false>::::()=E2=80=99 In my more minimal test case the error is more terse and less clear. :62:59: error: non-constant condition for static assertion 62 | static_assert(Flatten({{1, 2, 3}, {}, {4, 5}}).sum() =3D=3D 1 = + 2 + 3 + 4 + 5); |=20=20=20=20=20=20=20=20=20=20=20=20=20=20 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~ :62:51: error: '(((const std::vector >*)(&)) !=3D 0)' is not a constant expression 62 | static_assert(Flatten({{1, 2, 3}, {}, {4, 5}}).sum() =3D=3D 1 = + 2 + 3 + 4 + 5); |=20=20=20=20=20=20=20=20=20=20=20 ```cpp #include #include template struct VectorIter { constexpr std::optional next() { if (front =3D=3D back) return std::optional(); T& item =3D v[front]; front +=3D 1u; return std::optional(std::move(item)); } constexpr VectorIter(std::vector v2) : v(std::move(v2)), front(0u), back(v.size()) {} VectorIter(VectorIter&&) =3D default; VectorIter& operator=3D(VectorIter&&) =3D default; std::vector v; size_t front; size_t back; }; template struct Flatten { constexpr Flatten(std::vector> v) : vec(std::move(v)) {} constexpr std::optional next() { std::optional out; while (true) { // Take an item off front_iter_ if possible. if (front_iter_.has_value()) { out =3D front_iter_.value().next(); if (out.has_value()) return out; front_iter_ =3D std::nullopt; } // Otherwise grab the next vector into front_iter_. if (!vec.empty()) { std::vector v =3D std::move(vec[0]); vec.erase(vec.begin()); front_iter_.emplace([](auto&& iter) { return VectorIter(std::move(iter)); }(std::move(v))); } if (!front_iter_.has_value()) break; } return out; } constexpr T sum() && { T out =3D T(); while (true) { std::optional i =3D next(); if (!i.has_value()) break; out +=3D *i; } return out; } std::vector> vec; std::optional> front_iter_; }; static_assert(Flatten({{1, 2, 3}, {}, {4, 5}}).sum() =3D=3D 1 + 2 + 3 = + 4 + 5); int main() {} ``` When the Flatten::next() method is simplified a bit, so that it can see the union is only initialized once, the GCC compiler no longer rejects the code. https://gcc.godbolt.org/z/szfGsdxb7 ```cpp #include #include template struct VectorIter { constexpr std::optional next() { if (front =3D=3D back) return std::optional(); T& item =3D v[front]; front +=3D 1u; return std::optional(std::move(item)); } constexpr VectorIter(std::vector v2) : v(std::move(v2)), front(0u), back(v.size()) {} VectorIter(VectorIter&&) =3D default; VectorIter& operator=3D(VectorIter&&) =3D default; std::vector v; size_t front; size_t back; }; template struct Flatten { constexpr Flatten(std::vector v) : vec(std::move(v)) {} constexpr std::optional next() { std::optional out; while (true) { // Take an item off front_iter_ if possible. if (front_iter_.has_value()) { out =3D front_iter_.value().next(); if (out.has_value()) return out; front_iter_ =3D std::nullopt; } // Otherwise grab the next vector into front_iter_. if (!moved) { std::vector v =3D std::move(vec); moved =3D true; front_iter_.emplace([](auto&& iter) { return VectorIter(std::move(iter)); }(std::move(v))); } if (!front_iter_.has_value()) break; } return out; } constexpr T sum() && { T out =3D T(); while (true) { std::optional i =3D next(); if (!i.has_value()) break; out +=3D *i; } return out; } bool moved =3D false; std::vector vec; std::optional> front_iter_; }; static_assert(Flatten({1, 2, 3}).sum() =3D=3D 1 + 2 + 3); int main() {} ``` Yet in the first example, the GCC compiler still rejects the code if only a single vector is passed in, so that the union is only initialized once, in = the same way as the 2nd example: ``` static_assert(Flatten({{1, 2, 3}}).sum() =3D=3D 1 + 2 + 3); ```=