Bug 68877 - [6 Regression] swap for multidimensional array of int ill-formed
Summary: [6 Regression] swap for multidimensional array of int ill-formed
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 6.0
: P3 normal
Target Milestone: 6.0
Assignee: Daniel Krügler
URL:
Keywords: rejects-valid
Depends on:
Blocks:
 
Reported: 2015-12-12 18:31 UTC by Daniel Krügler
Modified: 2016-01-12 21:22 UTC (History)
1 user (show)

See Also:
Host:
Target:
Build:
Known to work: 4.9.2, 5.1.0, 5.2.0
Known to fail: 6.0
Last reconfirmed: 2015-12-12 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Daniel Krügler 2015-12-12 18:31:08 UTC
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.
Comment 1 Jonathan Wakely 2015-12-12 19:19:07 UTC
Sounds good to me, thanks! :-)
Comment 2 Marc Glisse 2015-12-12 20:10:47 UTC
I suggest (this is not a joke) that the right fix is to implement noexcept(auto) as an extension (possibly available only in system headers for now), and use it inside the library.
Comment 3 Daniel Krügler 2015-12-14 20:51:13 UTC
A patch has been sent to the patch list.
Comment 4 Jonathan Wakely 2016-01-12 21:20:29 UTC
Author: redi
Date: Tue Jan 12 21:19:58 2016
New Revision: 232296

URL: https://gcc.gnu.org/viewcvs?rev=232296&root=gcc&view=rev
Log:
libstdc++/68877 Reimplement std::__is_swappable

2016-01-12  Daniel Kruegler  <daniel.kruegler@gmail.com>

	PR libstdc++/68877
	* include/std/type_traits: Following N4511, reimplement __is_swappable
	and __is_nothrow_swappable. Move __is_swappable to namespace std,
	adjust callers. Use __is_nothrow_swappable in swap.
	* include/bits/move.h: Use __is_nothrow_swappable in swap.
	* testsuite/20_util/is_nothrow_swappable/value.cc: Extend; remove
	__is_swappable related tests.
	* testsuite/20_util/is_swappable/value.cc: New.
	* testsuite/20_util/is_swappable/requirements/
	explicit_instantiation.cc: New.
	* testsuite/20_util/is_swappable/requirements/typedefs.cc: New.
	* testsuite/25_algorithms/swap/68877.cc: New.

Added:
    trunk/libstdc++-v3/testsuite/20_util/is_swappable/
    trunk/libstdc++-v3/testsuite/20_util/is_swappable/requirements/
    trunk/libstdc++-v3/testsuite/20_util/is_swappable/requirements/explicit_instantiation.cc
    trunk/libstdc++-v3/testsuite/20_util/is_swappable/requirements/typedefs.cc
    trunk/libstdc++-v3/testsuite/20_util/is_swappable/value.cc
    trunk/libstdc++-v3/testsuite/25_algorithms/swap/68877.cc
Modified:
    trunk/libstdc++-v3/ChangeLog
    trunk/libstdc++-v3/include/bits/move.h
    trunk/libstdc++-v3/include/std/type_traits
    trunk/libstdc++-v3/testsuite/20_util/is_nothrow_swappable/value.cc
Comment 5 Jonathan Wakely 2016-01-12 21:22:12 UTC
Fixed