From c7a6a32739d62deab03266e2b5449fce261b1ecb Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Wed, 16 Mar 2022 09:34:34 -0400 Subject: [PATCH] c++: alias template and empty parameter packs [PR104008] Zero-length pack expansions are treated as if no list were provided at all, that is, with template struct S { }; template void g() { S...>; } g will result in S<>. In the following test we have something similar: template using IsOneOf = disjunction...>; and then we have "IsOneOf..." where OtherHolders is an empty pack. Since r11-7931, we strip_typedefs in TYPE_PACK_EXPANSION. In this test that results in "IsOneOf" being turned into "disjunction<>". So the whole expansion is now "disjunction<>...". But then we error in make_pack_expansion because find_parameter_packs_r won't find the pack OtherHolders. We strip the alias template because dependent_alias_template_spec_p says it's not dependent. It it not dependent because this alias is not TEMPLATE_DECL_COMPLEX_ALIAS_P. My understanding is that currently we consider an alias complex if it 1) expands a pack from the enclosing class, as in template typename... TT> struct S { template using X = P...>; }; where the alias expands TT; or 2) the expansion does *not* name all the template parameters, as in template struct R; template using U = R...>; where T is not named in the expansion. But IsOneOf is neither. And it can't know how it's going to be used. Therefore I think we cannot make it complex (and in turn dependent) to fix this bug. After much gnashing of teeth, I think we simply want to avoid stripping the alias if the new pattern doesn't have any parameter packs to expand. PR c++/104008 gcc/cp/ChangeLog: * tree.cc (strip_typedefs): Don't strip an alias template when doing so would result in losing a parameter pack. gcc/testsuite/ChangeLog: * g++.dg/cpp0x/variadic-alias3.C: New test. * g++.dg/cpp0x/variadic-alias4.C: New test. --- gcc/cp/tree.cc | 13 +++++++- gcc/testsuite/g++.dg/cpp0x/variadic-alias3.C | 45 ++++++++++++++++++++++++++ gcc/testsuite/g++.dg/cpp0x/variadic-alias4.C | 48 ++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic-alias3.C create mode 100644 gcc/testsuite/g++.dg/cpp0x/variadic-alias4.C diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index 6e9be71..eb59e56 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -1778,7 +1778,18 @@ strip_typedefs (tree t, bool *remove_attributes, unsigned int flags) if (TYPE_P (pat)) { type = strip_typedefs (pat, remove_attributes, flags); - if (type != pat) + /* Empty packs can thwart our efforts here. Consider + + template + using IsOneOf = disjunction...>; + + where IsOneOf seemingly uses all of its template parameters in + its expansion (and does not expand a pack from the enclosing + class), so the alias is not marked as complex. However, it may + be used as in "IsOneOf", where Ts is an empty parameter pack, + and stripping it down into "disjunction<>" here would exclude the + Ts pack, resulting in an error. */ + if (type != pat && uses_parameter_packs (type)) { result = copy_node (t); PACK_EXPANSION_PATTERN (result) = type; diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic-alias3.C b/gcc/testsuite/g++.dg/cpp0x/variadic-alias3.C new file mode 100644 index 0000000..6b6dd9f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic-alias3.C @@ -0,0 +1,45 @@ +// PR c++/104008 +// { dg-do compile { target c++11 } } + +template struct conjunction; +template struct disjunction; +template struct is_same; +template struct enable_if; +template using enable_if_t = typename enable_if<_Cond>::type; +struct B; +struct __uniq_ptr_impl { + struct _Ptr { + using type = B *; + }; + using pointer = _Ptr::type; +}; +struct unique_ptr { + using pointer = __uniq_ptr_impl::pointer; + unique_ptr(pointer); +}; +template +using IsOneOf = disjunction...>; + +template struct any_badge; + +struct badge { + badge(any_badge<>); + badge(); +}; + +template struct any_badge { + template ...>::value>> + any_badge(); +}; + +template unique_ptr make_unique(_Args... __args); + +struct B { + B(badge); + unique_ptr b_ = make_unique(badge{}); +}; + +template unique_ptr make_unique(_Args... __args) { + return new B(__args...); +} diff --git a/gcc/testsuite/g++.dg/cpp0x/variadic-alias4.C b/gcc/testsuite/g++.dg/cpp0x/variadic-alias4.C new file mode 100644 index 0000000..896a472 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/variadic-alias4.C @@ -0,0 +1,48 @@ +// PR c++/104008 +// { dg-do compile { target c++11 } } +// Differs from variadic-alias3.C only in the pattern of a pack expansion +// in line 34. But it's important to check that we also deal with more +// complex patterns. + +template struct conjunction; +template struct disjunction; +template struct is_same; +template struct enable_if; +template using enable_if_t = typename enable_if<_Cond>::type; +struct B; +struct __uniq_ptr_impl { + struct _Ptr { + using type = B *; + }; + using pointer = _Ptr::type; +}; +struct unique_ptr { + using pointer = __uniq_ptr_impl::pointer; + unique_ptr(pointer); +}; +template +using IsOneOf = disjunction...>; + +template struct any_badge; + +struct badge { + badge(any_badge<>); + badge(); +}; + +template struct any_badge { + template &...>::value>> + any_badge(); +}; + +template unique_ptr make_unique(_Args... __args); + +struct B { + B(badge); + unique_ptr b_ = make_unique(badge{}); +}; + +template unique_ptr make_unique(_Args... __args) { + return new B(__args...); +} -- 2.7.4