unique_ptr assignment with move-assignable but not convertible deleters

classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

unique_ptr assignment with move-assignable but not convertible deleters

Aleksey Demakov
Hi all,

I found what seems to be a problem with C++ library in both gcc-8.3
and 9.1 (while clang 7.0 is ok).

Some background info. I use unique_ptrs to pass messages between 2
threads. In the source thread I use one deleter in the target thread
another one. I use unique_ptr assignment at the moment the message
passes the boundary between threads.

I think that it should be possible in C++17 as long as the source
deleter is move-assignable to the target deleter. However stdc++ for
some reason additionally requires them to be convertible.

The code to reproduce the problen is below.

Regards,
Aleksey

-----

#include <iostream>
#include <memory>
#include <type_traits>

// Setting this enables compilation
#define WORKAROUND 0

struct D;

struct E {
        void operator()(void *) {
        }

#if WORKAROUND
        // Never defined, just a declaration to fool type checks in the lib.
        operator D();
#endif
};

struct D {
        void operator=(E&&) noexcept {
        }

        void operator()(void *) {
        }
};

int
main()
{
        std::unique_ptr<void, D> t(nullptr, D());
        std::unique_ptr<void, E> u(nullptr, E());

        std::cout
                << "convertibe pointers: "
                << std::is_convertible_v<decltype(u)::pointer,
                                        decltype(t)::pointer>
                << "\n";
        // This one shoudn't be required, but it is for some reason.
        std::cout
                << "convertibe deleters: "
                << std::is_convertible_v<E, D>
                << "\n";
        std::cout
                << "assignable deleters: "
                << std::is_assignable_v<D&, E&&>
                << "\n";

        t = std::move(u);

        return 0;
}

-----

$ g++ -std=c++17 u.cc
u.cc:46:17: error: no match for 'operator=' (operand types are
'std::unique_ptr<void, D>' and
'std::remove_reference<std::unique_ptr<void, E>&>::type' {aka
'std::unique_ptr<void, E>'})
   46 |  t = std::move(u);
      |                 ^
In file included from /opt/local/include/gcc9/c++/memory:80,
                 from u.cc:2:
/opt/local/include/gcc9/c++/bits/unique_ptr.h:302:7: note: candidate:
'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp,
_Dp>::operator=(std::unique_ptr<_Tp, _Dp>&&) [with _Tp = void; _Dp =
D]'
  302 |       operator=(unique_ptr&& __u) noexcept
      |       ^~~~~~~~
/opt/local/include/gcc9/c++/bits/unique_ptr.h:302:30: note:   no known
conversion for argument 1 from
'std::remove_reference<std::unique_ptr<void, E>&>::type' {aka
'std::unique_ptr<void, E>'} to 'std::unique_ptr<void, D>&&'
  302 |       operator=(unique_ptr&& __u) noexcept
      |                 ~~~~~~~~~~~~~^~~
/opt/local/include/gcc9/c++/bits/unique_ptr.h:322:2: note: candidate:
'template<class _Up, class _Ep> typename
std::enable_if<std::__and_<std::__and_<std::is_convertible<typename
std::unique_ptr<_Up, _Ep>::pointer, typename std::__uniq_ptr_impl<_Tp,
_Dp>::pointer>, std::__not_<std::is_array<_Up> >,
std::__or_<std::__and_<std::is_reference<_Dp>, std::is_same<_T2, _U2>
>, std::__and_<std::__not_<std::is_reference<_Dp> >,
std::is_convertible<_Ep, _Dp> > > >, std::is_assignable<_T2&, _U2&&>
>::value, std::unique_ptr<_Tp, _Dp>&>::type std::unique_ptr<_Tp,
_Dp>::operator=(std::unique_ptr<_Up, _Ep>&&) [with _Up = _Up; _Ep =
_Ep; _Tp = void; _Dp = D]'
  322 |  operator=(unique_ptr<_Up, _Ep>&& __u) noexcept
      |  ^~~~~~~~
/opt/local/include/gcc9/c++/bits/unique_ptr.h:322:2: note:   template
argument deduction/substitution failed:
/opt/local/include/gcc9/c++/bits/unique_ptr.h: In substitution of
'template<class _Up, class _Ep> typename
std::enable_if<std::__and_<std::__and_<std::is_convertible<typename
std::unique_ptr<_Tp, _Dp>::pointer, void*>,
std::__not_<std::is_array<_Tp> >,
std::__or_<std::__and_<std::is_reference<D>, std::is_same<D, _U2> >,
std::__and_<std::__not_<std::is_reference<D> >,
std::is_convertible<_Ep, D> > > >, std::is_assignable<D&, _U2&&>
>::value, std::unique_ptr<void, D>&>::type std::unique_ptr<void,
D>::operator=<_Up, _Ep>(std::unique_ptr<_Tp, _Dp>&&) [with _Up = void;
_Ep = E]':
u.cc:46:17:   required from here
/opt/local/include/gcc9/c++/bits/unique_ptr.h:322:2: error: no type
named 'type' in 'struct std::enable_if<false, std::unique_ptr<void,
D>&>'
/opt/local/include/gcc9/c++/bits/unique_ptr.h:331:7: note: candidate:
'std::unique_ptr<_Tp, _Dp>& std::unique_ptr<_Tp,
_Dp>::operator=(std::nullptr_t) [with _Tp = void; _Dp = D;
std::nullptr_t = std::nullptr_t]'
  331 |       operator=(nullptr_t) noexcept
      |       ^~~~~~~~
/opt/local/include/gcc9/c++/bits/unique_ptr.h:331:17: note:   no known
conversion for argument 1 from
'std::remove_reference<std::unique_ptr<void, E>&>::type' {aka
'std::unique_ptr<void, E>'} to 'std::nullptr_t'
  331 |       operator=(nullptr_t) noexcept
      |                 ^~~~~~~~~


-----

$ clang++ -std=c++17 u.cc
$ #everyting's ok.

-----
Reply | Threaded
Open this post in threaded view
|

Re: unique_ptr assignment with move-assignable but not convertible deleters

Jonathan Wakely-4
On Tue, 30 Jul 2019 at 09:07, Aleksey Demakov wrote:
>
> Hi all,
>
> I found what seems to be a problem with C++ library in both gcc-8.3
> and 9.1 (while clang 7.0 is ok).

I assume by clang you mean clang with libc++, because clang with
libstdc++ fails in the same way as GCC with libstdc++.

Please report a bug as described at https://gcc.gnu.org/bugs/

The problem is that the unique_ptr::operator=(unique_ptr<U, E>&&)
assignment operator incorrectly includes this constraint from the
converting constructor:
http://eel.is/c++draft/unique.ptr.single#ctor-19.3

The assignment operator is supposed to have slightly different constraints:
http://eel.is/c++draft/unique.ptr.single#asgn-6

Thanks!
Reply | Threaded
Open this post in threaded view
|

Re: unique_ptr assignment with move-assignable but not convertible deleters

Jonathan Wakely-4
On Wed, 31 Jul 2019 at 10:53, Jonathan Wakely wrote:

>
> On Tue, 30 Jul 2019 at 09:07, Aleksey Demakov wrote:
> >
> > Hi all,
> >
> > I found what seems to be a problem with C++ library in both gcc-8.3
> > and 9.1 (while clang 7.0 is ok).
>
> I assume by clang you mean clang with libc++, because clang with
> libstdc++ fails in the same way as GCC with libstdc++.
>
> Please report a bug as described at https://gcc.gnu.org/bugs/
>
> The problem is that the unique_ptr::operator=(unique_ptr<U, E>&&)
> assignment operator incorrectly includes this constraint from the
> converting constructor:
> http://eel.is/c++draft/unique.ptr.single#ctor-19.3
>
> The assignment operator is supposed to have slightly different constraints:
> http://eel.is/c++draft/unique.ptr.single#asgn-6
>
> Thanks!

Reported as https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91308
Reply | Threaded
Open this post in threaded view
|

Re: unique_ptr assignment with move-assignable but not convertible deleters

Jonathan Wakely-3
On 31/07/19 13:49 +0100, Jonathan Wakely wrote:

>On Wed, 31 Jul 2019 at 10:53, Jonathan Wakely wrote:
>>
>> On Tue, 30 Jul 2019 at 09:07, Aleksey Demakov wrote:
>> >
>> > Hi all,
>> >
>> > I found what seems to be a problem with C++ library in both gcc-8.3
>> > and 9.1 (while clang 7.0 is ok).
>>
>> I assume by clang you mean clang with libc++, because clang with
>> libstdc++ fails in the same way as GCC with libstdc++.
>>
>> Please report a bug as described at https://gcc.gnu.org/bugs/
>>
>> The problem is that the unique_ptr::operator=(unique_ptr<U, E>&&)
>> assignment operator incorrectly includes this constraint from the
>> converting constructor:
>> http://eel.is/c++draft/unique.ptr.single#ctor-19.3
>>
>> The assignment operator is supposed to have slightly different constraints:
>> http://eel.is/c++draft/unique.ptr.single#asgn-6
>>
>> Thanks!
>
>Reported as https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91308
And fixed by this patch. Tested x86_64-linux, committed to trunk.
Backports to follow soon.



patch.txt (4K) Download Attachment
Reply | Threaded
Open this post in threaded view
|

Re: unique_ptr assignment with move-assignable but not convertible deleters

Aleksey Demakov
On Wed, Jul 31, 2019 at 5:39 PM Jonathan Wakely <[hidden email]> wrote:
> And fixed by this patch. Tested x86_64-linux, committed to trunk.
> Backports to follow soon.
>

That was fast! Thank you very much.

Regards,
Aleksey