public inbox for gcc-bugs@sourceware.org
help / color / mirror / Atom feed
* [Bug c++/110905] New: GCC rejects constexpr code that may re-initialize union member
@ 2023-08-04 18:57 danakj at orodu dot net
  2023-08-04 19:02 ` [Bug c++/110905] " pinskia at gcc dot gnu.org
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: danakj at orodu dot net @ 2023-08-04 18:57 UTC (permalink / raw)
  To: gcc-bugs

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);
```

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Bug c++/110905] GCC rejects constexpr code that may re-initialize union member
  2023-08-04 18:57 [Bug c++/110905] New: GCC rejects constexpr code that may re-initialize union member danakj at orodu dot net
@ 2023-08-04 19:02 ` pinskia at gcc dot gnu.org
  2023-08-04 20:41 ` danakj at orodu dot net
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: pinskia at gcc dot gnu.org @ 2023-08-04 19:02 UTC (permalink / raw)
  To: gcc-bugs

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

Andrew Pinski <pinskia at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
   Last reconfirmed|                            |2023-08-04
             Status|UNCONFIRMED                 |WAITING
     Ever confirmed|0                           |1

--- Comment #1 from Andrew Pinski <pinskia at gcc dot gnu.org> ---
>In my more minimal test case the error is more terse and less clear.


The reduced testcase is a different issue and is a dup of bug 85944.

In the first testcase provided below if we move the static_assert into main
instead of the toplevel, it gets accepted.

I think you need to redo your reduction.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Bug c++/110905] GCC rejects constexpr code that may re-initialize union member
  2023-08-04 18:57 [Bug c++/110905] New: GCC rejects constexpr code that may re-initialize union member danakj at orodu dot net
  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
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: danakj at orodu dot net @ 2023-08-04 20:41 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #2 from danakj at orodu dot net ---
Ah ok. Here's a big reproduction: https://godbolt.org/z/Kj7Tcd6P4

/opt/compiler-explorer/gcc-trunk-20230804/include/c++/14.0.0/bits/stl_construct.h:97:14:
  in 'constexpr' expansion of
'((sus::containers::VecIntoIter<sus::num::i32>*)<anonymous>)->sus::containers::VecIntoIter<sus::num::i32>::VecIntoIter((*
& std::forward<sus::containers::VecIntoIter<sus::num::i32> >((* & __args#0))))'
<source>:32895:22: error: use of deleted function
'sus::option::__private::Storage<sus::containers::VecIntoIter<sus::num::i32>,
false>::<unnamed union>::<constructor>()'
32895 | struct [[nodiscard]] VecIntoIter final
      |                      ^~~~~~~~~~~
<source>:3015:9: note:
'sus::option::__private::Storage<sus::containers::VecIntoIter<sus::num::i32>,
false>::<unnamed union>::<constructor>()' is implicitly deleted because the
default definition would be ill-formed:
 3015 |   union {
      |         ^
<source>:3015:9: error: no matching function for call to
'sus::containers::VecIntoIter<sus::num::i32>::VecIntoIter()'
<source>:32953:13: note: candidate: 'constexpr
sus::containers::VecIntoIter<ItemT>::VecIntoIter(sus::containers::Vec<ItemT>&&,
sus::num::usize, sus::num::usize) [with ItemT = sus::num::i32]'
32953 |   constexpr VecIntoIter(Vec<Item>&& vec, usize front, usize back)
noexcept
      |             ^~~~~~~~~~~
<source>:32953:13: note:   candidate expects 3 arguments, 0 provided
<source>:32951:13: note: candidate: 'constexpr
sus::containers::VecIntoIter<ItemT>::VecIntoIter(sus::containers::Vec<ItemT>&&)
[with ItemT = sus::num::i32]'
32951 |   constexpr VecIntoIter(Vec<Item>&& vec) noexcept :
vec_(::sus::move(vec)) {}
      |             ^~~~~~~~~~~
<source>:32951:13: note:   candidate expects 1 argument, 0 provided
<source>:32895:22: note: candidate: 'constexpr
sus::containers::VecIntoIter<sus::num::i32>::VecIntoIter(sus::containers::VecIntoIter<sus::num::i32>&&)'
32895 | struct [[nodiscard]] VecIntoIter final
      |                      ^~~~~~~~~~~
<source>:32895:22: note:   candidate expects 1 argument, 0 provided
Compiler returned: 1


I will try to shrink it now.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Bug c++/110905] GCC rejects constexpr code that may re-initialize union member
  2023-08-04 18:57 [Bug c++/110905] New: GCC rejects constexpr code that may re-initialize union member danakj at orodu dot net
  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
  4 siblings, 0 replies; 6+ messages in thread
From: danakj at orodu dot net @ 2023-08-04 22:30 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #3 from danakj at orodu dot net ---
Repro down from 37k to under 1000 lines now: https://godbolt.org/z/enMxaqjb6

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Bug c++/110905] GCC rejects constexpr code that may re-initialize union member
  2023-08-04 18:57 [Bug c++/110905] New: GCC rejects constexpr code that may re-initialize union member danakj at orodu dot net
                   ` (2 preceding siblings ...)
  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
  4 siblings, 0 replies; 6+ messages in thread
From: danakj at orodu dot net @ 2023-08-04 23:09 UTC (permalink / raw)
  To: gcc-bugs

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

--- Comment #4 from danakj at orodu dot net ---
Ok it only happens if the VecIntoIter class has a base class, even when it's
empty the bug reproduces. But when I remove the IteratorBase base class, then
GCC compiles it correctly.

It's getting hard to remove anything and keep it to repro at this point.
Replacing Option with std::optional also made it stop, for whatever reason.

https://godbolt.org/z/a9PcsKTMf

```
#include <concepts>
#include <cstddef>
#include <memory>
#include <utility>

template <class T>
class Vec;

constexpr int from_sum(auto&& it) noexcept {
    auto p = int(0);
    while (true) {
        auto i = it.next();
        if (!i.has_value()) break;
        p += *i;
    }
    return p;
}

template <class T>
struct Storage final {
    constexpr ~Storage()
        requires(std::is_trivially_destructible_v<T>)
    = default;
    constexpr ~Storage()
        requires(!std::is_trivially_destructible_v<T>)
    {}

    constexpr Storage(const Storage&)
        requires(std::is_trivially_copy_constructible_v<T>)
    = default;
    constexpr Storage& operator=(const Storage&)
        requires(std::is_trivially_copy_assignable_v<T>)
    = default;
    constexpr Storage(Storage&&)
        requires(std::is_trivially_move_constructible_v<T>)
    = default;
    constexpr Storage& operator=(Storage&&)
        requires(std::is_trivially_move_assignable_v<T>)
    = default;

    constexpr Storage() {}
    constexpr Storage(const std::remove_cvref_t<T>& t)
        : val_(t), state_(true) {}
    constexpr Storage(std::remove_cvref_t<T>& t) : val_(t), state_(true) {}
    constexpr Storage(std::remove_cvref_t<T>&& t)
        : val_(std::move(t)), state_(true) {}

    __attribute__((pure)) constexpr const T& val() const { return val_; }
    __attribute__((pure)) constexpr T& val_mut() { return val_; }

    __attribute__((pure)) constexpr inline bool state() const noexcept {
        return state_;
    }

    constexpr inline void construct_from_none(const T& t) noexcept
        requires(std::is_copy_constructible_v<T>)
    {
        std::construct_at(&val_, t);
        state_ = true;
    }
    constexpr inline void construct_from_none(T&& t) noexcept {
        std::construct_at(&val_, std::move(t));
        state_ = true;
    }

    constexpr inline void set_some(const T& t) noexcept
        requires(std::is_copy_constructible_v<T>)
    {
        if (state_ == false)
            construct_from_none(t);
        else
            val_ = t;
        state_ = true;
    }
    constexpr inline void set_some(T&& t) noexcept {
        if (state_ == false)
            construct_from_none(std::move(t));
        else
            val_ = std::move(t);
        state_ = true;
    }

    [[nodiscard]] constexpr inline T replace_some(T&& t) noexcept {
        return std::exchange(val_, std::move(t));
    }

    [[nodiscard]] constexpr inline T take_and_set_none() noexcept {
        state_ = false;
        auto taken = T(static_cast<T&&>(val_));
        val_.~T();
        return taken;
    }

    constexpr inline void set_none() noexcept {
        state_ = false;
        val_.~T();
    }

    constexpr inline void destroy() noexcept { val_.~T(); }

   private:
    union {
        T val_;
    };
    bool state_ = false;
};

template <class T>
class Option final {
    static_assert(!std::is_reference_v<T>);
    static_assert(!std::is_const_v<T>);

   public:
    inline constexpr Option() noexcept = default;

    static inline constexpr Option with(const T& t) noexcept
        requires(std::is_copy_constructible_v<T>)
    {
        return Option(t);
    }

    static inline constexpr Option with(T&& t) noexcept {
        if constexpr (std::is_move_constructible_v<T>) {
            return Option(std::move(t));
        } else {
            return Option(t);
        }
    }

    constexpr ~Option() noexcept
        requires(std::is_trivially_destructible_v<T>)
    = default;

    constexpr inline ~Option() noexcept
        requires(!std::is_trivially_destructible_v<T>)
    {
        if (t_.state()) t_.destroy();
    }

    constexpr Option(Option&& o)
        requires(std::is_trivially_move_constructible_v<T>)
    = default;

    constexpr Option(Option&& o) noexcept
        requires(!std::is_trivially_move_constructible_v<T>)
    {
        if (o.t_.state()) t_.construct_from_none(o.t_.take_and_set_none());
    }

    constexpr Option(Option&& o)
        requires(!std::is_move_constructible_v<T>)
    = delete;

    constexpr Option& operator=(Option&& o)
        requires(std::is_trivially_move_assignable_v<T>)
    = default;

    constexpr Option& operator=(Option&& o) noexcept
        requires(!std::is_trivially_move_assignable_v<T>)
    {
        if (o.t_.state())
            t_.set_some(o.t_.take_and_set_none());
        else if (t_.state())
            t_.set_none();
        return *this;
    }

    constexpr Option& operator=(Option&& o)
        requires(!std::is_move_constructible_v<T>)
    = delete;

    __attribute__((pure)) constexpr bool has_value() const noexcept {
        return t_.state();
    }

    __attribute__((pure)) constexpr const std::remove_reference_t<T>&
    operator*() const& noexcept {
        return t_.val();
    }
    __attribute__((pure)) constexpr std::remove_reference_t<T>&
    operator*() & noexcept {
        return t_.val_mut();
    }

   private:
    template <class U>
    friend class Option;

    constexpr explicit Option(const T& t) : t_(t) {}
    constexpr explicit Option(T&& t) : t_(std::move(t)) {}

    Storage<T> t_;
};

template <class Iter, class Item>
class IteratorBase {
   public:
};

template <class EachIter, class InnerSizedIter>
class [[nodiscard]] Flatten
    : public IteratorBase<Flatten<EachIter, InnerSizedIter>,
                          typename EachIter::Item> {
   public:
    using Item = typename EachIter::Item;

    constexpr Flatten(InnerSizedIter&& iters) : iters_(std::move(iters)) {}

    constexpr Option<Item> next() noexcept {
        Option<Item> out;
        while (true) {
            if (front_iter_.has_value()) {
                out = (*front_iter_).next();
                if (out.has_value()) return out;
                front_iter_ = Option<EachIter>();
            }

            front_iter_ = [](auto&& i) {
                if (!i.has_value()) return Option<EachIter>();
                return Option<EachIter>::with(std::move(*i).into_iter());
            }(iters_.next());
            if (!front_iter_.has_value()) break;
        }

        return out;
    }

    constexpr Item sum() && noexcept { return from_sum(std::move(*this)); }

   private:
    InnerSizedIter iters_;
    Option<EachIter> front_iter_;
};

template <class ItemT>
struct [[nodiscard]] VecIntoIter
    : public IteratorBase<VecIntoIter<ItemT>, ItemT> {
   public:
    using Item = ItemT;

    static constexpr auto with(Vec<Item>&& vec) noexcept {
        return VecIntoIter(std::move(vec));
    }

    constexpr Option<Item> next() noexcept {
        if (front_index_ == back_index_) [[unlikely]]
            return Option<Item>();

        Item& item =
            vec_[std::exchange(front_index_, front_index_ + size_t{1})];
        return Option<Item>::with(std::move(item));
    }

    constexpr auto flatten() && noexcept {
        using Flatten = Flatten<
            decltype(std::declval<std::remove_cvref_t<Item>&&>().into_iter()),
            VecIntoIter>;
        return Flatten(std::move(*this));
    }

   private:
    constexpr VecIntoIter(Vec<Item>&& vec) noexcept : vec_(std::move(vec)) {}

    Vec<Item> vec_;
    size_t front_index_ = size_t{0};
    size_t back_index_ = vec_.len();
};

template <class T>
class Vec final {
    static_assert(!std::is_reference_v<T>);
    static_assert(!std::is_const_v<T>);

   public:
    template <std::convertible_to<T>... Ts>
    static inline constexpr Vec with(Ts&&... values) noexcept {
        auto v = Vec(nullptr, size_t{0}, size_t{0});
        v.grow_to_exact(sizeof...(Ts));
        (..., v.push(std::forward<Ts>(values)));
        return v;
    }

    constexpr ~Vec() {
        if (is_alloced()) free_storage();
    }

    constexpr Vec(Vec&& o) noexcept
        : data_(std::exchange(o.data_, nullptr)),
          len_(std::exchange(o.len_, 0)),
          capacity_(std::exchange(o.capacity_, 0)) {}
    constexpr Vec& operator=(Vec&& o) noexcept {
        if (is_alloced()) free_storage();
        data_ = std::exchange(o.data_, nullptr);
        len_ = std::exchange(o.len_, 0);
        capacity_ = std::exchange(o.capacity_, 0);
        return *this;
    }

    constexpr void grow_to_exact(size_t cap) noexcept {
        if (cap <= capacity_) return;
        const auto bytes = sizeof(T) * cap;

        if (!is_alloced()) {
            data_ = std::allocator<T>().allocate(size_t{cap});
            capacity_ = cap;
            return;
        }

        T* new_allocation = std::allocator<T>().allocate(size_t{cap});
        T* old_t = data_;
        T* new_t = new_allocation;

        const size_t self_len = len();
        for (size_t i = 0; i < self_len; i += 1u) {
            std::construct_at(new_t, std::move(*old_t));
            old_t->~T();
            ++old_t;
            ++new_t;
        }
        std::allocator<T>().deallocate(data_, size_t{capacity_});
        data_ = new_allocation;
        capacity_ = cap;
    }

    constexpr void reserve(size_t additional) noexcept {
        if (len() + additional <= capacity_) return;
        grow_to_exact(apply_growth_function(additional));
    }

    constexpr void push(T t) noexcept
        requires(std::is_move_constructible_v<T>)
    {
        reserve(size_t{1});
        const auto self_len = len();
        std::construct_at(data_ + self_len, std::move(t));
        len_ = self_len + size_t{1};
    }

    constexpr VecIntoIter<T> into_iter() && noexcept
        requires(std::is_move_constructible_v<T>)
    {
        return VecIntoIter<T>::with(std::move(*this));
    }

    __attribute__((pure)) constexpr T& operator[](size_t i) & noexcept {
        return *(data_ + i);
    }

    __attribute__((pure)) constexpr inline size_t len() const& noexcept {
        return len_;
    }

   private:
    constexpr Vec(T* ptr, size_t len, size_t cap)
        : data_(ptr), len_(len), capacity_(cap) {}

    constexpr size_t apply_growth_function(size_t additional) const noexcept {
        size_t goal = additional + len();
        size_t cap = capacity_;

        while (cap < goal) {
            cap = (cap + 1u) * 3u;
            auto bytes = sizeof(T) * cap;
        }
        return cap;
    }

    constexpr void free_storage() {
        if constexpr (!std::is_trivially_destructible_v<T>) {
            const auto self_len = len();
            for (size_t i = 0; i < self_len; i += 1u) (data_ + i)->~T();
        }
        std::allocator<T>().deallocate(data_, size_t{capacity_});
    }

    constexpr inline bool is_alloced() const noexcept {
        return capacity_ > size_t{0};
    }

    T* data_;
    size_t len_;
    size_t capacity_;
};

int main() {
    static_assert(Vec<Vec<int>>::with(Vec<int>::with(1, 2, 3),  //
                                      Vec<int>::with(4),        //
                                      Vec<int>::with(),         //
                                      Vec<int>::with(5, 6))
                      .into_iter()
                      .flatten()
                      .sum() == 1 + 2 + 3 + 4 + 5 + 6);
}
```

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [Bug c++/110905] GCC rejects constexpr code that may re-initialize union member
  2023-08-04 18:57 [Bug c++/110905] New: GCC rejects constexpr code that may re-initialize union member danakj at orodu dot net
                   ` (3 preceding siblings ...)
  2023-08-04 23:09 ` danakj at orodu dot net
@ 2024-03-08 15:44 ` ppalka at gcc dot gnu.org
  4 siblings, 0 replies; 6+ messages in thread
From: ppalka at gcc dot gnu.org @ 2024-03-08 15:44 UTC (permalink / raw)
  To: gcc-bugs

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

Patrick Palka <ppalka at gcc dot gnu.org> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
           See Also|                            |https://gcc.gnu.org/bugzill
                   |                            |a/show_bug.cgi?id=101631
                 CC|                            |ppalka at gcc dot gnu.org

--- Comment #5 from Patrick Palka <ppalka at gcc dot gnu.org> ---
GCC trunk accepts the comment #4 testcase (thanks for the reduction) since
r14-4771.

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2024-03-08 15:44 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-08-04 18:57 [Bug c++/110905] New: GCC rejects constexpr code that may re-initialize union member danakj at orodu dot net
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

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).