[Bug libstdc++/68877] New: swap for multidimensional array of int ill-formed

daniel.kruegler at googlemail dot com gcc-bugzilla@gcc.gnu.org
Sat Dec 12 18:31:00 GMT 2015


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

            Bug ID: 68877
           Summary: swap for multidimensional array of int ill-formed
           Product: gcc
           Version: 6.0
            Status: UNCONFIRMED
          Severity: normal
          Priority: P3
         Component: libstdc++
          Assignee: unassigned at gcc dot gnu.org
          Reporter: daniel.kruegler at googlemail dot com
  Target Milestone: ---

The following code example (A reduced form of an example from a recent LWG
issue http://cplusplus.github.io/LWG/lwg-active.html#2554)

/-----------------------------------------------
#include <utility>

int main() 
{
  int x[2][3];
  int y[2][3];

  std::swap(x, y);
}
/-----------------------------------------------

compiled with the flags

-Wall -Wextra

and targetting any of C++1z, C++14, or C++11 with or without GNU extensions is
rejected in 

gcc HEAD 6.0.0 20151207 (experimental)

(but not in previous releases such as 5.2.0) with the compiler diagnostics:

<quote>
In file included from /usr/local/gcc-head/include/c++/6.0.0/bits/move.h:57:0,
                 from /usr/local/gcc-head/include/c++/6.0.0/bits/stl_pair.h:59,
                 from /usr/local/gcc-head/include/c++/6.0.0/utility:70,
                 from prog.cc:1:
/usr/local/gcc-head/include/c++/6.0.0/type_traits: In instantiation of
'typename
std::enable_if<std::__is_swappable_impl::__is_swappable<_Tp>::value>::type
std::swap(_Tp (&)[_Nm], _Tp (&)[_Nm]) [with _Tp = int [2]; long unsigned int
_Nm = 1ul; typename
std::enable_if<std::__is_swappable_impl::__is_swappable<_Tp>::value>::type =
void]':
prog.cc:8:17:   required from here
/usr/local/gcc-head/include/c++/6.0.0/type_traits:2594:27: error: no matching
function for call to 'swap(int [2], int [2])'
     noexcept(noexcept(swap(*__a, *__b)));
                       ~~~~^~~~~~~~~~~~

In file included from
/usr/local/gcc-head/include/c++/6.0.0/bits/stl_pair.h:59:0,
                 from /usr/local/gcc-head/include/c++/6.0.0/utility:70,
                 from prog.cc:1:
/usr/local/gcc-head/include/c++/6.0.0/bits/move.h:179:5: note: candidate:
template<class _Tp> typename
std::enable_if<std::__and_<std::is_move_constructible<_Tp>,
std::is_move_assignable<_Tp> >::value>::type std::swap(_Tp&, _Tp&)
     swap(_Tp& __a, _Tp& __b)
     ^~~~

/usr/local/gcc-head/include/c++/6.0.0/bits/move.h:179:5: note:   template
argument deduction/substitution failed:
/usr/local/gcc-head/include/c++/6.0.0/bits/move.h: In substitution of
'template<class _Tp> typename
std::enable_if<std::__and_<std::is_move_constructible<_Tp>,
std::is_move_assignable<_Tp> >::value>::type std::swap(_Tp&, _Tp&) [with _Tp =
int [2]]':
/usr/local/gcc-head/include/c++/6.0.0/type_traits:2594:27:   required from
'typename
std::enable_if<std::__is_swappable_impl::__is_swappable<_Tp>::value>::type
std::swap(_Tp (&)[_Nm], _Tp (&)[_Nm]) [with _Tp = int [2]; long unsigned int
_Nm = 1ul; typename
std::enable_if<std::__is_swappable_impl::__is_swappable<_Tp>::value>::type =
void]'
prog.cc:8:17:   required from here
/usr/local/gcc-head/include/c++/6.0.0/bits/move.h:179:5: error: no type named
'type' in 'struct std::enable_if<false, void>'
In file included from /usr/local/gcc-head/include/c++/6.0.0/bits/move.h:57:0,
                 from /usr/local/gcc-head/include/c++/6.0.0/bits/stl_pair.h:59,
                 from /usr/local/gcc-head/include/c++/6.0.0/utility:70,
                 from prog.cc:1:
/usr/local/gcc-head/include/c++/6.0.0/type_traits: In instantiation of
'typename
std::enable_if<std::__is_swappable_impl::__is_swappable<_Tp>::value>::type
std::swap(_Tp (&)[_Nm], _Tp (&)[_Nm]) [with _Tp = int [2]; long unsigned int
_Nm = 1ul; typename
std::enable_if<std::__is_swappable_impl::__is_swappable<_Tp>::value>::type =
void]':
prog.cc:8:17:   required from here
/usr/local/gcc-head/include/c++/6.0.0/type_traits:2594:27: error: no matching
function for call to 'swap(int [2], int [2])'
     noexcept(noexcept(swap(*__a, *__b)));
                       ~~~~^~~~~~~~~~~~

In file included from
/usr/local/gcc-head/include/c++/6.0.0/bits/stl_pair.h:59:0,
                 from /usr/local/gcc-head/include/c++/6.0.0/utility:70,
                 from prog.cc:1:
/usr/local/gcc-head/include/c++/6.0.0/bits/move.h:179:5: note: candidate:
template<class _Tp> typename
std::enable_if<std::__and_<std::is_move_constructible<_Tp>,
std::is_move_assignable<_Tp> >::value>::type std::swap(_Tp&, _Tp&)
     swap(_Tp& __a, _Tp& __b)
     ^~~~

/usr/local/gcc-head/include/c++/6.0.0/bits/move.h:179:5: note:   template
argument deduction/substitution failed:
/usr/local/gcc-head/include/c++/6.0.0/bits/move.h: In substitution of
'template<class _Tp> typename
std::enable_if<std::__and_<std::is_move_constructible<_Tp>,
std::is_move_assignable<_Tp> >::value>::type std::swap(_Tp&, _Tp&) [with _Tp =
int [2]]':
/usr/local/gcc-head/include/c++/6.0.0/type_traits:2594:27:   required from
'typename
std::enable_if<std::__is_swappable_impl::__is_swappable<_Tp>::value>::type
std::swap(_Tp (&)[_Nm], _Tp (&)[_Nm]) [with _Tp = int [2]; long unsigned int
_Nm = 1ul; typename
std::enable_if<std::__is_swappable_impl::__is_swappable<_Tp>::value>::type =
void]'
prog.cc:8:17:   required from here
/usr/local/gcc-head/include/c++/6.0.0/bits/move.h:179:5: error: no type named
'type' in 'struct std::enable_if<false, void>'
</quote>

It seems to me that gcc's extension usage of a constrained swap declaration
(which is generally an improvement over what the standard specifies) interacts
nastily with the currently required (but obviously defect, see issue above)
exception-specification by the Standard Library. The effect of the
expression-form swap(*__a, *__b) used in the function declaration

template<typename _Tp, size_t _Nm>
    inline
    typename enable_if<__is_swappable_impl::__is_swappable<_Tp>::value>::type
    swap(_Tp (&__a)[_Nm], _Tp (&__b)[_Nm])
    noexcept(noexcept(swap(*__a, *__b)));

is, that lookup cannot yet see the just being declared swap overload for arrays
(because the declaration is not completed within the noexcept declaration),
therefore the expression

swap(*__a, *__b)

is effectively evaluated in the context of the previously declared non-array
overload:

template<typename _Tp>
    inline
    typename enable_if<__and_<is_move_constructible<_Tp>,
                              is_move_assignable<_Tp>>::value>::type
    swap(_Tp&, _Tp&)
    noexcept(__and_<is_nothrow_move_constructible<_Tp>,
                    is_nothrow_move_assignable<_Tp>>::value);

But this form is constrained in regard to is_move_constructible<_Tp> and
is_move_assignable<_Tp>, none of which can evaluate to true for arrays. 

A possible fix would be to (a) remove the swap constraints (which presumably
has other unwanted effects) or (b) replace the required noexcept expression
noexcept(swap(*__a, *__b)) of the array form by
__is_nothrow_swappable<_Tp>::value as suggested in the paper

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4511.html

Being the author of that paper I'm interested to add a more complete
__is_[nothrow_]swappable to libstdc++ that would also work for this example. So
feel free to assign me to this bug.


More information about the Gcc-bugs mailing list