https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85183
Bug ID: 85183 Summary: [8 Regression] std::variant move assignment mishandles move-only types Product: gcc Version: 8.0 Status: UNCONFIRMED Keywords: rejects-valid Severity: normal Priority: P3 Component: libstdc++ Assignee: unassigned at gcc dot gnu.org Reporter: rs2740 at gmail dot com Target Milestone: --- Repro: #include <variant> struct moveonly { moveonly() noexcept { } moveonly(moveonly&&) noexcept { } moveonly& operator=(moveonly&&) noexcept { return *this; } ~moveonly() { } }; void test() { std::variant<moveonly> m1, m2; m1 = std::move(m2); } Compiles with 7. Breaks with 8: In file included from prog.cc:1: /opt/wandbox/gcc-head/include/c++/8.0.1/variant: In instantiation of 'void std::__detail::__variant::__erased_assign(void*, void*) [with _Lhs = moveonly&; _Rhs = const moveonly&]': /opt/wandbox/gcc-head/include/c++/8.0.1/variant:607:27: required from 'std::__detail::__variant::_Move_assign_base<<anonymous>, _Types>& std::__detail::__variant::_Move_assign_base<<anonymous>, _Types>::operator=(std::__detail::__variant::_Move_assign_base<<anonymous>, _Types>&&) [with bool <anonymous> = false; _Types = {moveonly}]' /opt/wandbox/gcc-head/include/c++/8.0.1/variant:648:12: required from here /opt/wandbox/gcc-head/include/c++/8.0.1/variant:258:42: error: use of deleted function 'constexpr moveonly& moveonly::operator=(const moveonly&)' __variant::__ref_cast<_Lhs>(__lhs) = __variant::__ref_cast<_Rhs>(__rhs); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ prog.cc:3:8: note: 'constexpr moveonly& moveonly::operator=(const moveonly&)' is implicitly declared as deleted because 'moveonly' declares a move constructor or move assignment operator struct moveonly { ^~~~~~~~ In file included from prog.cc:1: /opt/wandbox/gcc-head/include/c++/8.0.1/variant: In instantiation of 'void std::__detail::__variant::__erased_ctor(void*, void*) [with _Lhs = moveonly&; _Rhs = const moveonly&]': /opt/wandbox/gcc-head/include/c++/8.0.1/variant:468:30: required from 'std::__detail::__variant::_Copy_ctor_base<<anonymous>, _Types>::_Copy_ctor_base(const std::__detail::__variant::_Copy_ctor_base<<anonymous>, _Types>&) [with bool <anonymous> = false; _Types = {moveonly}]' /opt/wandbox/gcc-head/include/c++/8.0.1/variant:509:7: required from 'std::__detail::__variant::_Move_assign_base<<anonymous>, _Types>& std::__detail::__variant::_Move_assign_base<<anonymous>, _Types>::operator=(std::__detail::__variant::_Move_assign_base<<anonymous>, _Types>&&) [with bool <anonymous> = false; _Types = {moveonly}]' /opt/wandbox/gcc-head/include/c++/8.0.1/variant:648:12: required from here /opt/wandbox/gcc-head/include/c++/8.0.1/variant:246:7: error: use of deleted function 'constexpr moveonly::moveonly(const moveonly&)' ::new (__lhs) _Type(__variant::__ref_cast<_Rhs>(__rhs)); ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ prog.cc:3:8: note: 'constexpr moveonly::moveonly(const moveonly&)' is implicitly declared as deleted because 'moveonly' declares a move constructor or move assignment operator struct moveonly { ---- There are two bugs in the move assignment operator of _Move_assign_base primary template, presumably caused by copy-paste: * in the matching index case, it uses __erased_assign<_Types&, const _Types&> when it should be using __erased_assign<_Types&, _Types&&>. * in the mismatched index case, it copies __rhs to create __tmp (`_Move_assign_base __tmp(__rhs);`) when it should have moved from it.