Search code examples
c++gccg++stdc++20

Does this use of optional<string> reveal a bug in gcc?


I've reduced my code to the following example, which fails -O3 C++20 compilation on my g++ (x64 12.3) as well as apparently 14.1 when using godbolt:

Again according to godbolt, clang works without any warnings. Is the warning valid, or a bug in GCC?

Here is my code. Apologies for the length; I tried to be minimal, but the warning is not predictable nor easy to trigger.


#include <cassert>
#include <cstdlib>
#include <optional>
#include <string>
#include <utility>

using namespace std;

class HS {
   public:
    constexpr HS() = default;

    constexpr explicit HS(string_view view) : _view(view) {}

    constexpr HS(HS const& other) { *this = other; }

    constexpr HS& operator=(HS const& other) {
        _stringOpt = other._stringOpt;
        _view = _stringOpt ? string_view{*_stringOpt} : other._view;
        return *this;
    }

    constexpr HS& operator=(HS&& other) noexcept {
        if (this != &other) {
            _stringOpt =
                std::exchange(/*inout*/ other._stringOpt, std::nullopt);
            _view = _stringOpt ? string_view{*_stringOpt} : other._view;
            other._view = k_empty;
        }
        return *this;
    }

   private:
    static constexpr char const* k_empty = "";

    optional<string> _stringOpt;
    string_view _view = k_empty;
};

inline HS operator""_hs(char const* string, size_t length) noexcept {
    return HS(string_view{string, length});
}

HS bar(HS const& hs) { return rand() < 10000 ? "sdok"_hs : hs; }

int func() {
    HS foo;
    try {
        foo = "1"_hs;
        foo = bar(foo);        
        foo = "2"_hs;  // <--- warning is about this
    } catch (...) {
        return 1;
    }
    return 0;
}

// No warning if body of func() is placed inside main()
int main() { return func(); }

I get this warning (treated as error):

In file included from /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/string:54,
                 from <source>:5:
In member function 'constexpr std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size_type std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::size() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]',
    inlined from 'constexpr std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator __sv_type() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]' at /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/bits/basic_string.h:950:16,
    inlined from 'constexpr HS& HS::operator=(HS&&)' at <source>:28:57,
    inlined from 'constexpr HS& HS::operator=(HS&&)' at <source>:24:19,
    inlined from 'int func()' at <source>:52:15:
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/bits/basic_string.h:1077:16: error: '*(const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)((char*)&foo + offsetof(HS, HS::_stringOpt.std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::<unnamed>.std::_Optional_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, false, false>::<unnamed>)).std::__cxx11::basic_string<char>::_M_string_length' may be used uninitialized [-Werror=maybe-uninitialized]
 1077 |       { return _M_string_length; }
      |                ^~~~~~~~~~~~~~~~
<source>: In function 'int func()':
<source>:48:8: note: 'foo' declared here
   48 |     HS foo;
      |        ^~~
In member function 'constexpr std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::pointer std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::_M_data() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]',
    inlined from 'constexpr const _CharT* std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::data() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]' at /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/bits/basic_string.h:2654:23,
    inlined from 'constexpr std::__cxx11::basic_string<_CharT, _Traits, _Alloc>::operator __sv_type() const [with _CharT = char; _Traits = std::char_traits<char>; _Alloc = std::allocator<char>]' at /opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/bits/basic_string.h:950:16,
    inlined from 'constexpr HS& HS::operator=(HS&&)' at <source>:28:57,
    inlined from 'constexpr HS& HS::operator=(HS&&)' at <source>:24:19,
    inlined from 'int func()' at <source>:52:15:
/opt/compiler-explorer/gcc-14.1.0/include/c++/14.1.0/bits/basic_string.h:228:28: error: '*(const std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >*)((char*)&foo + offsetof(HS, HS::_stringOpt.std::optional<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > >::<unnamed>.std::_Optional_base<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, false, false>::<unnamed>)).std::__cxx11::basic_string<char>::_M_dataplus.std::__cxx11::basic_string<char>::_Alloc_hider::_M_p' may be used uninitialized [-Werror=maybe-uninitialized]
  228 |       { return _M_dataplus._M_p; }
      |                            ^~~~
<source>: In function 'int func()':
<source>:48:8: note: 'foo' declared here
   48 |     HS foo;
      |        ^~~
cc1plus: all warnings being treated as errors
Compiler returned: 1

Solution

  • Again according to godbolt, clang works without any warnings. Is the warning valid, or a bug in GCC?

    Yes, this is GCC Bug 109561 that has been reported.