PR c++/56782 - Regression with empty pack expansions
In the example of the patch below, during the instantiation of is_convertible at #1, we see at some point Tuple<>. (Let's note '{}' an empty argument pack.) In that context, during the partial specialization the member template template<class... U> Tuple<>::Tuple<U, typename enable_if<and_<is_convertible<U, {}>... >::value, int >::type > Let's look at what happens to the expansion "is_convertible<U, {}>...." To express the result of that expansion tsubst_pack_expansion receives the expansion is_convertible<U, T>, with the argument list [{}]. This function should detect that we have an empty argument pack for the parameter pack T and no argument pack for the parameter pack U. It should thus return a pack expansion "is_convertible<U,T>..." that has this information: "I have gotten an argument list, that is not complete because U doesn't have any argument pack; the argument pack for T is '{}', so I'll wait for the next time I am passed to tsubst_pack_expansion with enough additional argument packs, to really perform the substitution". That information is conveyed by attaching the the '{}' to the PACK_EXPANSION_EXTRA property of the pack expansion returned by tsubst_pack_expansion. The problem in this report is that we are not setting PACK_EXPANSION_EXTRA when the non-complete argument pack list is made of an empty argument pack, because use_pack_expansion_extra_args_p doesn't detect this case. Fixed thus. gcc/cp/ * pt.c (use_pack_expansion_extra_args_p): When at least a parameter pack has an empty argument pack, and another parameter pack has no argument pack at all, use the PACK_EXPANSION_EXTRA mechanism. From-SVN: r198956
This commit is contained in:
parent
ad56ed7ebc
commit
f5b6d0d1d2
3 changed files with 79 additions and 7 deletions
|
@ -1,3 +1,11 @@
|
|||
2013-05-16 Dodji Seketeli <dodji@redhat.com>
|
||||
|
||||
PR c++/56782 - Regression with empty pack expansions
|
||||
* pt.c (use_pack_expansion_extra_args_p): When at least a
|
||||
parameter pack has an empty argument pack, and another parameter
|
||||
pack has no argument pack at all, use the PACK_EXPANSION_EXTRA
|
||||
mechanism.
|
||||
|
||||
2013-05-15 Paolo Carlini <paolo.carlini@oracle.com>
|
||||
|
||||
* name-lookup.c (pushdecl_maybe_friend_1): Replace pairs of
|
||||
|
|
15
gcc/cp/pt.c
15
gcc/cp/pt.c
|
@ -9202,8 +9202,15 @@ use_pack_expansion_extra_args_p (tree parm_packs,
|
|||
int arg_pack_len,
|
||||
bool has_empty_arg)
|
||||
{
|
||||
/* If one pack has an expansion and another pack has a normal
|
||||
argument or if one pack has an empty argument and an another
|
||||
one hasn't then tsubst_pack_expansion cannot perform the
|
||||
substitution and need to fall back on the
|
||||
PACK_EXPANSION_EXTRA mechanism. */
|
||||
if (parm_packs == NULL_TREE)
|
||||
return false;
|
||||
else if (has_empty_arg)
|
||||
return true;
|
||||
|
||||
bool has_expansion_arg = false;
|
||||
for (int i = 0 ; i < arg_pack_len; ++i)
|
||||
|
@ -9221,13 +9228,7 @@ use_pack_expansion_extra_args_p (tree parm_packs,
|
|||
has_non_expansion_arg = true;
|
||||
}
|
||||
|
||||
/* If one pack has an expansion and another pack has a normal
|
||||
argument or if one pack has an empty argument another one
|
||||
hasn't then tsubst_pack_expansion cannot perform the
|
||||
substitution and need to fall back on the
|
||||
PACK_EXPANSION_EXTRA mechanism. */
|
||||
if ((has_expansion_arg && has_non_expansion_arg)
|
||||
|| (has_empty_arg && (has_expansion_arg || has_non_expansion_arg)))
|
||||
if (has_expansion_arg && has_non_expansion_arg)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
63
gcc/testsuite/g++.dg/cpp0x/variadic143.C
Normal file
63
gcc/testsuite/g++.dg/cpp0x/variadic143.C
Normal file
|
@ -0,0 +1,63 @@
|
|||
// PR c++/56782
|
||||
// { dg-options -std=c++0x }
|
||||
|
||||
template<class T>
|
||||
T&& declval();
|
||||
|
||||
struct is_convertible_impl {
|
||||
template<class T>
|
||||
static void sink(T);
|
||||
|
||||
template<class T, class U, class = decltype(sink<U>(declval<T>()))>
|
||||
static auto test(int) -> char;
|
||||
|
||||
template<class, class>
|
||||
static auto test(...) -> char(&)[2];
|
||||
};
|
||||
|
||||
template<class T, class U>
|
||||
struct is_convertible : is_convertible_impl
|
||||
{
|
||||
static const bool value = sizeof(test<T, U>(0)) == 1;
|
||||
};
|
||||
|
||||
template<bool, class>
|
||||
struct enable_if {};
|
||||
|
||||
template<class T>
|
||||
struct enable_if<true, T> { typedef T type; };
|
||||
|
||||
template<bool, class If, class Else>
|
||||
struct conditional { typedef If type; };
|
||||
|
||||
template<class If, class Else>
|
||||
struct conditional<false, If, Else> { typedef Else type; };
|
||||
|
||||
template<class...>
|
||||
struct and_;
|
||||
|
||||
template<>
|
||||
struct and_<>
|
||||
{
|
||||
static const bool value = true;
|
||||
};
|
||||
|
||||
template<class P>
|
||||
struct and_<P> : P
|
||||
{
|
||||
};
|
||||
|
||||
template<class P1, class P2>
|
||||
struct and_<P1, P2> : conditional<P1::value, P2, P1>::type
|
||||
{
|
||||
};
|
||||
|
||||
template<class... T>
|
||||
struct Tuple {
|
||||
template<class... U,
|
||||
class = typename enable_if<and_<is_convertible<U, T>... >::value, int>::type
|
||||
>
|
||||
Tuple(U&&...){}
|
||||
};
|
||||
|
||||
static_assert(is_convertible<Tuple<>, Tuple<>>::value, "Ouch"); //#1
|
Loading…
Add table
Reference in a new issue