public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
From: "danakj at orodu dot net" <gcc-bugzilla@gcc.gnu.org>
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 [thread overview]
Message-ID: <bug-110905-4@http.gcc.gnu.org/bugzilla/> (raw)
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=110905
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 union
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
‘sus::option::__private::Storage<sus::containers::VecIntoIter<sus::num::i32>,
false>::<unnamed union>::<constructor>()’
In my more minimal test case the error is more terse and less clear.
<source>:62:59: error: non-constant condition for static assertion
62 | static_assert(Flatten<int>({{1, 2, 3}, {}, {4, 5}}).sum() == 1 + 2 + 3
+ 4 + 5);
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~
<source>:62:51: error: '(((const std::vector<int, std::allocator<int>
>*)(&<anonymous>)) != 0)' is not a constant expression
62 | static_assert(Flatten<int>({{1, 2, 3}, {}, {4, 5}}).sum() == 1 + 2 + 3
+ 4 + 5);
|
```cpp
#include <optional>
#include <vector>
template <class T>
struct VectorIter {
constexpr std::optional<T> next() {
if (front == back) return std::optional<T>();
T& item = v[front];
front += 1u;
return std::optional<T>(std::move(item));
}
constexpr VectorIter(std::vector<T> v2) : v(std::move(v2)), front(0u),
back(v.size()) {}
VectorIter(VectorIter&&) = default;
VectorIter& operator=(VectorIter&&) = default;
std::vector<T> v;
size_t front;
size_t back;
};
template <class T>
struct Flatten {
constexpr Flatten(std::vector<std::vector<T>> v) : vec(std::move(v)) {}
constexpr std::optional<T> next() {
std::optional<T> out;
while (true) {
// Take an item off front_iter_ if possible.
if (front_iter_.has_value()) {
out = front_iter_.value().next();
if (out.has_value()) return out;
front_iter_ = std::nullopt;
}
// Otherwise grab the next vector into front_iter_.
if (!vec.empty()) {
std::vector<T> v = std::move(vec[0]);
vec.erase(vec.begin());
front_iter_.emplace([](auto&& iter) {
return VectorIter<T>(std::move(iter));
}(std::move(v)));
}
if (!front_iter_.has_value()) break;
}
return out;
}
constexpr T sum() && {
T out = T();
while (true) {
std::optional<T> i = next();
if (!i.has_value()) break;
out += *i;
}
return out;
}
std::vector<std::vector<T>> vec;
std::optional<VectorIter<T>> front_iter_;
};
static_assert(Flatten<int>({{1, 2, 3}, {}, {4, 5}}).sum() == 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 <optional>
#include <vector>
template <class T>
struct VectorIter {
constexpr std::optional<T> next() {
if (front == back) return std::optional<T>();
T& item = v[front];
front += 1u;
return std::optional<T>(std::move(item));
}
constexpr VectorIter(std::vector<T> v2) : v(std::move(v2)), front(0u),
back(v.size()) {}
VectorIter(VectorIter&&) = default;
VectorIter& operator=(VectorIter&&) = default;
std::vector<T> v;
size_t front;
size_t back;
};
template <class T>
struct Flatten {
constexpr Flatten(std::vector<T> v) : vec(std::move(v)) {}
constexpr std::optional<T> next() {
std::optional<T> out;
while (true) {
// Take an item off front_iter_ if possible.
if (front_iter_.has_value()) {
out = front_iter_.value().next();
if (out.has_value()) return out;
front_iter_ = std::nullopt;
}
// Otherwise grab the next vector into front_iter_.
if (!moved) {
std::vector<T> v = std::move(vec);
moved = true;
front_iter_.emplace([](auto&& iter) {
return VectorIter<T>(std::move(iter));
}(std::move(v)));
}
if (!front_iter_.has_value()) break;
}
return out;
}
constexpr T sum() && {
T out = T();
while (true) {
std::optional<T> i = next();
if (!i.has_value()) break;
out += *i;
}
return out;
}
bool moved = false;
std::vector<T> vec;
std::optional<VectorIter<T>> front_iter_;
};
static_assert(Flatten<int>({1, 2, 3}).sum() == 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<int>({{1, 2, 3}}).sum() == 1 + 2 + 3);
```
next reply other threads:[~2023-08-04 18:57 UTC|newest]
Thread overview: 6+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-04 18:57 danakj at orodu dot net [this message]
2023-08-04 19:02 ` [Bug c++/110905] " pinskia at gcc dot gnu.org
2023-08-04 20:41 ` danakj at orodu dot net
2023-08-04 22:30 ` danakj at orodu dot net
2023-08-04 23:09 ` danakj at orodu dot net
2024-03-08 15:44 ` ppalka at gcc dot gnu.org
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=bug-110905-4@http.gcc.gnu.org/bugzilla/ \
--to=gcc-bugzilla@gcc.gnu.org \
--cc=gcc-bugs@gcc.gnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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).