markus@x4 tmp % cat subprocess.ii namespace std { template <typename _Tp, _Tp> struct integral_constant { static constexpr _Tp value = 0; }; template <typename> struct conditional; template <typename...> struct __or_; template <typename _B1, typename _B2> struct __or_<_B1, _B2> : conditional<_B2>::type {}; template <typename> struct is_enum : integral_constant<bool, __is_enum(int)> {}; template <typename> struct remove_reference; template <typename _Tp> struct remove_reference<_Tp &> { typedef _Tp type; }; template <typename _Tp> struct decay { typedef typename remove_reference<_Tp>::type type; }; template <typename> struct conditional { typedef is_enum<char> type; }; template <typename _Head> struct _Head_base { _Head _M_head_impl; }; template <unsigned long, typename...> struct _Tuple_impl; template <unsigned long _Idx, typename _Head, typename... _Tail> struct _Tuple_impl<_Idx, _Head, _Tail...> : _Tuple_impl<1, _Tail...>, _Head_base<_Head> { _Tuple_impl(_Tuple_impl &) = default; _Tuple_impl(_Tuple_impl &&__in) : _Tuple_impl(__in) {} }; template <unsigned long _Idx, typename _Head> struct _Tuple_impl<_Idx, _Head> {}; template <typename... _Elements> struct tuple : _Tuple_impl<0, _Elements...> {}; template <typename> struct _Bind; template <typename _Functor, typename... _Bound_args> struct _Bind<_Functor(_Bound_args...)> { tuple<_Bound_args...> _M_bound_args; }; template <typename _Tp, typename _Tp2 = typename decay<_Tp>::type> using __is_socketlike = __or_<integral_constant<bool, false>, is_enum<_Tp2>>; template <int, typename... _BoundArgs> struct _Bind_helper { typedef _Bind<int(typename decay<_BoundArgs>::type...)> type; }; template <typename _Func, typename... _BoundArgs> typename _Bind_helper<__is_socketlike<_Func>::value, _BoundArgs...>::type bind(_Func &&, _BoundArgs &&...); struct _Any_data { template <typename _Tp> _Tp _M_access() const; }; enum _Manager_operation {}; template <typename> struct function; struct _Function_base { template <typename _Functor> struct _Base_manager { static bool _M_manager(_Any_data &, const _Any_data &__source, _Manager_operation) { _Functor(*__source._M_access<_Functor *>()); } }; typedef bool (*_Manager_type)(_Any_data &, const _Any_data &, _Manager_operation); _Manager_type _M_manager; }; template <typename, typename> struct _Function_handler; template <typename _Res, typename _Functor, typename... _ArgTypes> struct _Function_handler<_Res(_ArgTypes...), _Functor> : _Function_base::_Base_manager<_Functor> {}; template <typename _Res, typename... _ArgTypes> struct function<_Res(_ArgTypes...)> : _Function_base { template <typename, typename> using _Requires = int; template <typename _Functor, typename = _Requires<int, void>, typename = _Requires<int, void>> function(_Functor); _Res operator()(_ArgTypes...); }; template <typename _Res, typename... _ArgTypes> template <typename _Functor, typename, typename> function<_Res(_ArgTypes...)>::function(_Functor) { _M_manager = _Function_handler<_Res(), _Functor>::_M_manager; } } struct Option { Option(Option &); }; using std::bind; using std::function; struct Subprocess {}; using InputFileDescriptors = Subprocess; using OutputFileDescriptors = int; void defaultClone(Option setup) { InputFileDescriptors stdinfds; OutputFileDescriptors stdoutfds, envp; function<int(function<int()>)> clone = 0; int pipes = clone(bind(envp, setup, stdinfds, stdoutfds, pipes)); } markus@x4 tmp % g++ -c subprocess.ii subprocess.ii: In copy constructor ‘std::_Tuple_impl<_Idx, _Head, _Tail ...>::_Tuple_impl(std::_Tuple_impl<_Idx, _Head, _Tail ...>&) [with long unsigned int _Idx = 0ul; _Head = Option; _Tail = {Subprocess, int, int}]’: subprocess.ii:21:3: internal compiler error: in assign_temp, at function.c:961 _Tuple_impl(_Tuple_impl &) = default; ^~~~~~~~~~~ 0xa51353 assign_temp(tree_node*, int, int) ../../gcc/gcc/function.c:961 0x9e9e25 expand_expr_real_1(tree_node*, rtx_def*, machine_mode, expand_modifier, rtx_def**, bool) ../../gcc/gcc/expr.c:10408 0x9f9048 expand_normal ../../gcc/gcc/expr.h:262 0x9f9048 store_field ../../gcc/gcc/expr.c:6679 0x9f4c7d expand_assignment(tree_node*, tree_node*, bool) ../../gcc/gcc/expr.c:5021 0x8e79de expand_gimple_stmt_1 ../../gcc/gcc/cfgexpand.c:3606 0x8e79de expand_gimple_stmt ../../gcc/gcc/cfgexpand.c:3702 0x8e9e88 expand_gimple_basic_block ../../gcc/gcc/cfgexpand.c:5708 0x8efe96 execute ../../gcc/gcc/cfgexpand.c:6323
Reduced testcase: template <typename T> struct A { T a; }; template <unsigned long, typename...> struct B; template <unsigned long N, typename T, typename... U> struct B<N, T, U...> : B<1, U...>, A<T> { B (B &) = default; B (B &&__in) : B(__in) {} }; template <unsigned long N, typename T> struct B<N, T> {}; struct C { C (C &); }; struct D {}; void foo (B<0, C, D, int, int> a) { B<0, C, D, int, int> b (a); }
Started with r228704, with ICEs like pr69851.C: In copy constructor ‘B<N, T, U ...>::B(B<N, T, U ...>&) [with long unsigned int N = 0ul; T = C; U = {D, int, int}]’: pr69851.C:8:3: internal compiler error: in store_field, at expr.c:6637 B (B &) = default; ^ 0xbc7f2a store_field ../../gcc/expr.c:6637 0xbc1947 expand_assignment(tree_node*, tree_node*, bool) ../../gcc/expr.c:5017 and in r232167 that changed to the current pr69851.C: In copy constructor ‘B<N, T, U ...>::B(B<N, T, U ...>&) [with long unsigned int N = 0ul; T = C; U = {D, int, int}]’: pr69851.C:8:3: internal compiler error: in assign_temp, at function.c:961 B (B &) = default; ^ 0xc80dc7 assign_temp(tree_node*, int, int) ../../gcc/function.c:961 0xc263d2 expand_expr_real_1(tree_node*, rtx_def*, machine_mode, expand_modifier, rtx_def**, bool) ../../gcc/expr.c:10411 0xc1bc42 expand_expr_real(tree_node*, rtx_def*, machine_mode, expand_modifier, rtx_def**, bool) ../../gcc/expr.c:7946 0xc03c84 expand_normal ../../gcc/expr.h:262 0xc17a0c store_field ../../gcc/expr.c:6678 0xc11114 expand_assignment(tree_node*, tree_node*, bool) ../../gcc/expr.c:5020 In any case, this is on the this_2(D)->D.2344 = _5(D)->D.2344; assignment where both the lhs and rhs are TREE_ADDRESSABLE typed fields 5 bytes long (i.e. without padding), while the actual type is 8 bytes long (with padding). Do we want a memcpy/memmove like assignment in this case, something else? As the assignment is without padding, during expansion we chose to store_bit_field it, but that means the rhs is evaluated using expand_normal, i.e. with NULL target, and that wants to create a temporary, because the type of the COMPONENT_REF is the type including padding, but the FIELD_DECL without padding.
(In reply to Jakub Jelinek from comment #2) > Do we want a memcpy/memmove like assignment in this case, something else? memcpy, yes. Why isn't that the default for whole bytes in memory, anyway?
(In reply to Jason Merrill from comment #3) > (In reply to Jakub Jelinek from comment #2) > > Do we want a memcpy/memmove like assignment in this case, something else? > > memcpy, yes. Why isn't that the default for whole bytes in memory, anyway? No idea. I guess at least for GCC 6 it might be safest to just hack up expand_assignment to expand TREE_ADDRESSABLE assignment that way (just ones with weirdo sizes, which would otherwise go into store_field, or all?).
Created attachment 37738 [details] gcc6-pr69851.patch Untested fix.
Author: jakub Date: Fri Feb 19 19:11:58 2016 New Revision: 233566 URL: https://gcc.gnu.org/viewcvs?rev=233566&root=gcc&view=rev Log: PR c++/69851 * expr.c (store_field): Don't use bit-field path if exp is COMPONENT_REF with TREE_ADDRESSABLE type, where TYPE_SIZE is different from bitsize, but DECL_SIZE of FIELD_DECL is bitsize and the assignment can be performed by bitwise copy. Formatting fix. * g++.dg/torture/pr69851.C: New test. Added: trunk/gcc/testsuite/g++.dg/torture/pr69851.C Modified: trunk/gcc/ChangeLog trunk/gcc/expr.c trunk/gcc/testsuite/ChangeLog
Fixed.
Thanks for the fast turn-around, that patch fixed the original issue like expected. I encountered another similar issue in the same code base which still causes an ICE, see 69889.