On Mon, 2017-12-11 at 21:10 -0500, Jason Merrill wrote:
> On 11/10/2017 04:45 PM, David Malcolm wrote:
> > The initial version of the patch kit added location wrapper nodes
> > around constants and uses-of-declarations, along with some other
> > places in the parser (typeid, alignof, sizeof, offsetof).
> >
> > This version takes a much more minimal approach: it only adds
> > location wrapper nodes around the arguments at callsites, thus
> > not adding wrapper nodes around uses of constants and decls in
> > other
> > locations.
> >
> > It keeps them for the other places in the parser (typeid, alignof,
> > sizeof, offsetof).
> >
> > In addition, for now, each site that adds wrapper nodes is guarded
> > with !processing_template_decl, suppressing the creation of wrapper
> > nodes when processing template declarations. This is to simplify
> > the patch kit so that we don't have to support wrapper nodes during
> > template expansion.
>
> Hmm, it should be easy to support them, since NON_LVALUE_EXPR and
> VIEW_CONVERT_EXPR don't otherwise appear in template trees.
>
> Jason
I don't know if it's "easy"; it's at least non-trivial.
I attempted to support them in the obvious way by adding the two codes
to the switch statement tsubst_copy, reusing the case used by NOP_EXPR
and others, but ran into a issue when dealing with template parameter
packs.
Attached is the reproducer I've been testing with (minimized using
"delta" from a stdlib reproducer); my code was failing with:
../../src/cp-stdlib.ii: In instantiation of ‘struct
allocator_traits<allocator<char> >’:
../../src/cp-stdlib.ii:31:8: required from ‘struct
__alloc_traits<allocator<char>, char>’
../../src/cp-stdlib.ii:43:75: required from ‘class basic_string<char,
allocator<char> >’
../../src/cp-stdlib.ii:47:58: required from here
../../src/cp-stdlib.ii:27:55: sorry, unimplemented: use of
‘type_pack_expansion’ in template
-> decltype(_S_construct(__a, __p, forward<_Args>(__args)...)) { }
^~~~~~
The issue is that normally "__args" would be a PARM_DECL of type
TYPE_PACK_EXPANSION, and that's handled by tsubst_decl, but on adding a
wrapper node we now have a VIEW_CONVERT_EXPR of the same type i.e.
TYPE_PACK_EXPANSION wrapping the PARM_DECL.
When tsubst traverses the tree, the VIEW_CONVERT_EXPR is reached first,
and it attempts to substitute the type TYPE_PACK_EXPANSION, which leads
to the "sorry".
If I understand things right, during substitution, only tsubst_decl on
PARM_DECL can handle nodes with type with code TYPE_PACK_EXPANSION.
The simplest approach seems to be to not create wrapper nodes for decls
of type TYPE_PACK_EXPANSION, and that seems to fix the issue.
Alternatively I can handle TYPE_PACK_EXPANSION for VIEW_CONVERT_EXPR in
tsubst by remapping the type to that of what they wrap after
substitution; doing so also fixes the issue.
Does this sound correct and sane? Do you have a preferred approach?
On fixing that I'm running into a different issue when testing the
stdlib (again with parameter packs, I think), but I'm still
investigating that one...
Thanks
Dave
typedef long unsigned int size_t;
template<typename _Tp>
struct remove_reference {};
template<typename _Tp>
constexpr _Tp&&
forward(typename remove_reference<_Tp>::type& __t) noexcept
{
}
struct __allocator_traits_base {
template<typename _Tp, typename _Up, typename = void>
struct __rebind
{
using type = typename _Tp::template rebind<_Up>::other;
};
};
template<typename _Alloc, typename _Up>
using __alloc_rebind = typename __allocator_traits_base::template
__rebind<_Alloc, _Up>::type;
template<typename _Alloc> struct allocator_traits {
template<typename _Tp> using rebind_alloc = __alloc_rebind<_Alloc, _Tp>;
template<typename _Tp, typename... _Args>
static auto construct(_Alloc& __a, _Tp* __p, _Args&&... __args)
-> decltype(_S_construct(__a, __p, forward<_Args>(__args)...)) { }
};
template<typename _Alloc, typename = typename _Alloc::value_type>
struct __alloc_traits : allocator_traits<_Alloc> {
typedef allocator_traits<_Alloc> _Base_type;
template<typename _Tp> struct rebind { typedef typename
_Base_type::template rebind_alloc<_Tp> other; };
};
template<typename _Tp> class allocator {
typedef _Tp value_type;
template<typename _Tp1> struct rebind { typedef allocator<_Tp1> other;
};
};
template<typename _CharT, typename _Alloc>
class basic_string {
typedef typename __alloc_traits<_Alloc>::template rebind<_CharT>::other
_Char_alloc_type;
};
template<size_t _Nw> struct _Base_bitset {
static void foo (basic_string<char, allocator<char> >) {}
};