Implement P0522R0, matching of template template arguments.

gcc/c-family/
	* c.opt (-fnew-ttp-matching): New flag.
	* c-opts.c (c_common_post_options): Default on if -std=c++1z.
gcc/cp/
	* pt.c (coerce_template_template_parms): Allow a template argument
	that's less specialized than the parameter.
	(unify_bound_ttp_args): Adjust parm's args to apply to arg's
	template.
	(coerce_template_args_for_ttp): Split out from
	lookup_template_class_1.
	(coerce_ttp_args_for_tta, store_defaulted_ttp)
	(lookup_defaulted_ttp, add_defaults_to_ttp): New.
	(process_partial_specialization): Set DECL_CONTEXT of
	template template-parameters.
	(coerce_template_parms): Only inform when complain.
	(expand_template_argument_pack): Handle error_mark_node.
	(convert_template_argument, template_args_equal, unify): Handle
	any_targ_node.
	* cp-tree.h (enum cp_tree_index): Add CPTI_ANY_TARG.
	(any_targ_node): New.
	* decl.c (cxx_init_decl_processing): Set it.
	* name-lookup.c (consider_binding_level): Ignore names with embedded
	spaces.

From-SVN: r243871
This commit is contained in:
Jason Merrill 2016-12-21 14:39:04 -05:00 committed by Jason Merrill
parent 3c75aaa3d8
commit 31bfc9b9dd
26 changed files with 417 additions and 59 deletions

View File

@ -1,3 +1,8 @@
2016-12-21 Jason Merrill <jason@redhat.com>
* c.opt (-fnew-ttp-matching): New flag.
* c-opts.c (c_common_post_options): Default on if -std=c++1z.
2016-12-14 Martin Jambor <mjambor@suse.cz>
* c-omp.c: Include omp-general.h instead of omp-low.h.

View File

@ -920,6 +920,10 @@ c_common_post_options (const char **pfilename)
if (!global_options_set.x_flag_new_inheriting_ctors)
flag_new_inheriting_ctors = abi_version_at_least (11);
/* For GCC 7, only enable DR150 resolution by default if -std=c++1z. */
if (!global_options_set.x_flag_new_ttp)
flag_new_ttp = (cxx_dialect >= cxx1z);
if (cxx_dialect >= cxx11)
{
/* If we're allowing C++0x constructs, don't warn about C++98

View File

@ -1443,6 +1443,10 @@ C++ ObjC++ Joined Ignore Warn(switch %qs is no longer supported)
fnew-abi
C++ ObjC++ Ignore Warn(switch %qs is no longer supported)
fnew-ttp-matching
C++ ObjC++ Var(flag_new_ttp)
Implement resolution of DR 150 for matching of template template arguments.
fnext-runtime
ObjC ObjC++ LTO Report RejectNegative Var(flag_next_runtime)
Generate code for NeXT (Apple Mac OS X) runtime environment.

View File

@ -1,5 +1,26 @@
2016-12-21 Jason Merrill <jason@redhat.com>
Implement P0522R0, matching of template template arguments.
* pt.c (coerce_template_template_parms): Allow a template argument
that's less specialized than the parameter.
(unify_bound_ttp_args): Adjust parm's args to apply to arg's
template.
(coerce_template_args_for_ttp): Split out from
lookup_template_class_1.
(coerce_ttp_args_for_tta, store_defaulted_ttp)
(lookup_defaulted_ttp, add_defaults_to_ttp): New.
(process_partial_specialization): Set DECL_CONTEXT of
template template-parameters.
(coerce_template_parms): Only inform when complain.
(expand_template_argument_pack): Handle error_mark_node.
(convert_template_argument, template_args_equal, unify): Handle
any_targ_node.
* cp-tree.h (enum cp_tree_index): Add CPTI_ANY_TARG.
(any_targ_node): New.
* decl.c (cxx_init_decl_processing): Set it.
* name-lookup.c (consider_binding_level): Ignore names with embedded
spaces.
PR c++/42329
* pt.c (unify_bound_ttp_args): Split out from unify.
(try_class_unification): Handle BOUND_TEMPLATE_TEMPLATE_PARM.

View File

@ -1140,6 +1140,8 @@ enum cp_tree_index
CPTI_ALIGN_TYPE,
CPTI_ANY_TARG,
CPTI_MAX
};
@ -1245,6 +1247,9 @@ extern GTY(()) tree cp_global_trees[CPTI_MAX];
#define keyed_classes cp_global_trees[CPTI_KEYED_CLASSES]
/* A node which matches any template argument. */
#define any_targ_node cp_global_trees[CPTI_ANY_TARG]
/* Node to indicate default access. This must be distinct from the
access nodes in tree.h. */

View File

@ -4161,6 +4161,9 @@ cxx_init_decl_processing (void)
global_type_node = make_node (LANG_TYPE);
record_unknown_type (global_type_node, "global type");
any_targ_node = make_node (LANG_TYPE);
record_unknown_type (any_targ_node, "any type");
/* Now, C++. */
current_lang_name = lang_name_cplusplus;

View File

@ -4758,8 +4758,10 @@ consider_binding_level (tree name, best_match <tree, tree> &bm,
&& DECL_ANTICIPATED (d))
continue;
if (DECL_NAME (d))
bm.consider (DECL_NAME (d));
if (tree name = DECL_NAME (d))
/* Ignore internal names with spaces in them. */
if (!strchr (IDENTIFIER_POINTER (name), ' '))
bm.consider (DECL_NAME (d));
}
}

View File

@ -3836,6 +3836,9 @@ check_for_bare_parameter_packs (tree t)
tree
expand_template_argument_pack (tree args)
{
if (args == error_mark_node)
return error_mark_node;
tree result_args = NULL_TREE;
int in_arg, out_arg = 0, nargs = args ? TREE_VEC_LENGTH (args) : 0;
int num_result_args = -1;
@ -4758,6 +4761,15 @@ process_partial_specialization (tree decl)
DECL_TEMPLATE_INFO (tmpl) = build_template_info (maintmpl, specargs);
DECL_PRIMARY_TEMPLATE (tmpl) = maintmpl;
/* Give template template parms a DECL_CONTEXT of the template
for which they are a parameter. */
for (i = 0; i < ntparms; ++i)
{
tree parm = TREE_VALUE (TREE_VEC_ELT (inner_parms, i));
if (TREE_CODE (parm) == TEMPLATE_DECL)
DECL_CONTEXT (parm) = tmpl;
}
if (VAR_P (decl))
/* We didn't register this in check_explicit_specialization so we could
wait until the constraints were set. */
@ -6863,11 +6875,164 @@ coerce_template_template_parm (tree parm,
return 1;
}
/* Coerce template argument list ARGLIST for use with template
template-parameter TEMPL. */
static tree
coerce_template_args_for_ttp (tree templ, tree arglist,
tsubst_flags_t complain)
{
/* Consider an example where a template template parameter declared as
template <class T, class U = std::allocator<T> > class TT
The template parameter level of T and U are one level larger than
of TT. To proper process the default argument of U, say when an
instantiation `TT<int>' is seen, we need to build the full
arguments containing {int} as the innermost level. Outer levels,
available when not appearing as default template argument, can be
obtained from the arguments of the enclosing template.
Suppose that TT is later substituted with std::vector. The above
instantiation is `TT<int, std::allocator<T> >' with TT at
level 1, and T at level 2, while the template arguments at level 1
becomes {std::vector} and the inner level 2 is {int}. */
tree outer = DECL_CONTEXT (templ);
if (outer)
{
if (DECL_TEMPLATE_SPECIALIZATION (outer))
/* We want arguments for the partial specialization, not arguments for
the primary template. */
outer = template_parms_to_args (DECL_TEMPLATE_PARMS (outer));
else
outer = TI_ARGS (get_template_info (DECL_TEMPLATE_RESULT (outer)));
}
else if (current_template_parms)
{
/* This is an argument of the current template, so we haven't set
DECL_CONTEXT yet. */
tree relevant_template_parms;
/* Parameter levels that are greater than the level of the given
template template parm are irrelevant. */
relevant_template_parms = current_template_parms;
while (TMPL_PARMS_DEPTH (relevant_template_parms)
!= TEMPLATE_TYPE_LEVEL (TREE_TYPE (templ)))
relevant_template_parms = TREE_CHAIN (relevant_template_parms);
outer = template_parms_to_args (relevant_template_parms);
}
if (outer)
arglist = add_to_template_args (outer, arglist);
tree parmlist = DECL_INNERMOST_TEMPLATE_PARMS (templ);
return coerce_template_parms (parmlist, arglist, templ,
complain,
/*require_all_args=*/true,
/*use_default_args=*/true);
}
/* A cache of template template parameters with match-all default
arguments. */
static GTY((deletable)) hash_map<tree,tree> *defaulted_ttp_cache;
static void
store_defaulted_ttp (tree v, tree t)
{
if (!defaulted_ttp_cache)
defaulted_ttp_cache = hash_map<tree,tree>::create_ggc (13);
defaulted_ttp_cache->put (v, t);
}
static tree
lookup_defaulted_ttp (tree v)
{
if (defaulted_ttp_cache)
if (tree *p = defaulted_ttp_cache->get (v))
return *p;
return NULL_TREE;
}
/* T is a bound template template-parameter. Copy its arguments into default
arguments of the template template-parameter's template parameters. */
static tree
add_defaults_to_ttp (tree otmpl)
{
if (tree c = lookup_defaulted_ttp (otmpl))
return c;
tree ntmpl = copy_node (otmpl);
tree ntype = copy_node (TREE_TYPE (otmpl));
TYPE_STUB_DECL (ntype) = TYPE_NAME (ntype) = ntmpl;
TYPE_MAIN_VARIANT (ntype) = ntype;
TYPE_POINTER_TO (ntype) = TYPE_REFERENCE_TO (ntype) = NULL_TREE;
TYPE_NAME (ntype) = ntmpl;
SET_TYPE_STRUCTURAL_EQUALITY (ntype);
tree idx = TEMPLATE_TYPE_PARM_INDEX (ntype)
= copy_node (TEMPLATE_TYPE_PARM_INDEX (ntype));
TEMPLATE_PARM_DECL (idx) = ntmpl;
TREE_TYPE (ntmpl) = TREE_TYPE (idx) = ntype;
tree oparms = DECL_TEMPLATE_PARMS (otmpl);
tree parms = DECL_TEMPLATE_PARMS (ntmpl) = copy_node (oparms);
TREE_CHAIN (parms) = TREE_CHAIN (oparms);
tree vec = TREE_VALUE (parms) = copy_node (TREE_VALUE (parms));
for (int i = 0; i < TREE_VEC_LENGTH (vec); ++i)
{
tree o = TREE_VEC_ELT (vec, i);
if (!template_parameter_pack_p (TREE_VALUE (o)))
{
tree n = TREE_VEC_ELT (vec, i) = copy_node (o);
TREE_PURPOSE (n) = any_targ_node;
}
}
store_defaulted_ttp (otmpl, ntmpl);
return ntmpl;
}
/* ARG is a bound potential template template-argument, and PARGS is a list
of arguments for the corresponding template template-parameter. Adjust
PARGS as appropriate for application to ARG's template, and if ARG is a
BOUND_TEMPLATE_TEMPLATE_PARM, possibly adjust it to add default template
arguments to the template template parameter. */
static tree
coerce_ttp_args_for_tta (tree& arg, tree pargs, tsubst_flags_t complain)
{
++processing_template_decl;
tree arg_tmpl = TYPE_TI_TEMPLATE (arg);
if (DECL_TEMPLATE_TEMPLATE_PARM_P (arg_tmpl))
{
/* When comparing two template template-parameters in partial ordering,
rewrite the one currently being used as an argument to have default
arguments for all parameters. */
arg_tmpl = add_defaults_to_ttp (arg_tmpl);
pargs = coerce_template_args_for_ttp (arg_tmpl, pargs, complain);
if (pargs != error_mark_node)
arg = bind_template_template_parm (TREE_TYPE (arg_tmpl),
TYPE_TI_ARGS (arg));
}
else
{
tree aparms
= INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (arg_tmpl));
pargs = coerce_template_parms (aparms, pargs, arg_tmpl, complain,
/*require_all*/true,
/*use_default*/true);
}
--processing_template_decl;
return pargs;
}
/* Subroutine of unify for the case when PARM is a
BOUND_TEMPLATE_TEMPLATE_PARM. */
static int
unify_bound_ttp_args (tree tparms, tree targs, tree parm, tree arg,
unify_bound_ttp_args (tree tparms, tree targs, tree parm, tree& arg,
bool explain_p)
{
tree parmvec = TYPE_TI_ARGS (parm);
@ -6878,10 +7043,27 @@ unify_bound_ttp_args (tree tparms, tree targs, tree parm, tree arg,
parmvec = expand_template_argument_pack (parmvec);
argvec = expand_template_argument_pack (argvec);
if (unify (tparms, targs, parmvec, argvec,
tree nparmvec = parmvec;
if (flag_new_ttp)
{
/* In keeping with P0522R0, adjust P's template arguments
to apply to A's template; then flatten it again. */
nparmvec = coerce_ttp_args_for_tta (arg, parmvec, tf_none);
nparmvec = expand_template_argument_pack (nparmvec);
}
if (unify (tparms, targs, nparmvec, argvec,
UNIFY_ALLOW_NONE, explain_p))
return 1;
/* If the P0522 adjustment eliminated a pack expansion, deduce
empty packs. */
if (flag_new_ttp
&& TREE_VEC_LENGTH (nparmvec) < TREE_VEC_LENGTH (parmvec)
&& unify_pack_expansion (tparms, targs, parmvec, argvec,
DEDUCE_EXACT, /*sub*/true, explain_p))
return 1;
return 0;
}
@ -6914,6 +7096,48 @@ coerce_template_template_parms (tree parm_parms,
nparms = TREE_VEC_LENGTH (parm_parms);
nargs = TREE_VEC_LENGTH (arg_parms);
if (flag_new_ttp)
{
/* P0522R0: A template template-parameter P is at least as specialized as
a template template-argument A if, given the following rewrite to two
function templates, the function template corresponding to P is at
least as specialized as the function template corresponding to A
according to the partial ordering rules for function templates
([temp.func.order]). Given an invented class template X with the
template parameter list of A (including default arguments):
* Each of the two function templates has the same template parameters,
respectively, as P or A.
* Each function template has a single function parameter whose type is
a specialization of X with template arguments corresponding to the
template parameters from the respective function template where, for
each template parameter PP in the template parameter list of the
function template, a corresponding template argument AA is formed. If
PP declares a parameter pack, then AA is the pack expansion
PP... ([temp.variadic]); otherwise, AA is the id-expression PP.
If the rewrite produces an invalid type, then P is not at least as
specialized as A. */
/* So coerce P's args to apply to A's parms, and then deduce between A's
args and the converted args. If that succeeds, A is at least as
specialized as P, so they match.*/
tree pargs = template_parms_level_to_args (parm_parms);
++processing_template_decl;
pargs = coerce_template_parms (arg_parms, pargs, NULL_TREE, tf_none,
/*require_all*/true, /*use_default*/true);
--processing_template_decl;
if (pargs != error_mark_node)
{
tree targs = make_tree_vec (nargs);
tree aargs = template_parms_level_to_args (arg_parms);
if (!unify (arg_parms, targs, aargs, pargs, UNIFY_ALLOW_NONE,
/*explain*/false))
return 1;
}
}
/* Determine whether we have a parameter pack at the end of the
template template parameter's template parameter list. */
if (TREE_VEC_ELT (parm_parms, nparms - 1) != error_mark_node)
@ -7172,6 +7396,9 @@ convert_template_argument (tree parm,
if (TREE_CODE (arg) == WILDCARD_DECL)
return convert_wildcard_argument (parm, arg);
if (arg == any_targ_node)
return arg;
if (TREE_CODE (arg) == TREE_LIST
&& TREE_CODE (TREE_VALUE (arg)) == OFFSET_REF)
{
@ -7822,8 +8049,9 @@ coerce_template_parms (tree parms,
in_decl);
if (conv == error_mark_node)
{
inform (input_location, "so any instantiation with a "
"non-empty parameter pack would be ill-formed");
if (complain & tf_error)
inform (input_location, "so any instantiation with a "
"non-empty parameter pack would be ill-formed");
++lost;
}
else if (TYPE_P (conv) && !TYPE_P (pattern))
@ -7987,6 +8215,8 @@ template_args_equal (tree ot, tree nt)
return 1;
if (nt == NULL_TREE || ot == NULL_TREE)
return false;
if (nt == any_targ_node || ot == any_targ_node)
return true;
if (TREE_CODE (nt) == TREE_VEC)
/* For member templates */
@ -8328,57 +8558,8 @@ lookup_template_class_1 (tree d1, tree arglist, tree in_decl, tree context,
if (DECL_TEMPLATE_TEMPLATE_PARM_P (templ))
{
/* Create a new TEMPLATE_DECL and TEMPLATE_TEMPLATE_PARM node to store
template arguments */
tree parm;
tree arglist2;
tree outer;
parmlist = DECL_INNERMOST_TEMPLATE_PARMS (templ);
/* Consider an example where a template template parameter declared as
template <class T, class U = std::allocator<T> > class TT
The template parameter level of T and U are one level larger than
of TT. To proper process the default argument of U, say when an
instantiation `TT<int>' is seen, we need to build the full
arguments containing {int} as the innermost level. Outer levels,
available when not appearing as default template argument, can be
obtained from the arguments of the enclosing template.
Suppose that TT is later substituted with std::vector. The above
instantiation is `TT<int, std::allocator<T> >' with TT at
level 1, and T at level 2, while the template arguments at level 1
becomes {std::vector} and the inner level 2 is {int}. */
outer = DECL_CONTEXT (templ);
if (outer)
outer = TI_ARGS (get_template_info (DECL_TEMPLATE_RESULT (outer)));
else if (current_template_parms)
{
/* This is an argument of the current template, so we haven't set
DECL_CONTEXT yet. */
tree relevant_template_parms;
/* Parameter levels that are greater than the level of the given
template template parm are irrelevant. */
relevant_template_parms = current_template_parms;
while (TMPL_PARMS_DEPTH (relevant_template_parms)
!= TEMPLATE_TYPE_LEVEL (TREE_TYPE (templ)))
relevant_template_parms = TREE_CHAIN (relevant_template_parms);
outer = template_parms_to_args (relevant_template_parms);
}
if (outer)
arglist = add_to_template_args (outer, arglist);
arglist2 = coerce_template_parms (parmlist, arglist, templ,
complain,
/*require_all_args=*/true,
/*use_default_args=*/true);
tree arglist2 = coerce_template_args_for_ttp (templ, arglist, complain);
if (arglist2 == error_mark_node
|| (!uses_template_parms (arglist2)
&& check_instantiated_args (templ, arglist2, complain)))
@ -19950,6 +20131,9 @@ unify (tree tparms, tree targs, tree parm, tree arg, int strict,
template args from other function args. */
return unify_success (explain_p);
if (parm == any_targ_node || arg == any_targ_node)
return unify_success (explain_p);
/* If PARM uses template parameters, then we can't bail out here,
even if ARG == PARM, since we won't record unifications for the
template parameters. We might need them if we're trying to

View File

@ -201,6 +201,7 @@ in the following sections.
-fno-implicit-inline-templates @gol
-fno-implement-inlines -fms-extensions @gol
-fnew-inheriting-ctors @gol
-fnew-ttp-matching @gol
-fno-nonansi-builtins -fnothrow-opt -fno-operator-names @gol
-fno-optional-diags -fpermissive @gol
-fno-pretty-templates @gol
@ -2455,6 +2456,14 @@ inheritance. This is part of C++17 but also considered to be a Defect
Report against C++11 and C++14. This flag is enabled by default
unless @option{-fabi-version=10} or lower is specified.
@item -fnew-ttp-matching
@opindex fnew-ttp-matching
Enable the P0522 resolution to Core issue 150, template template
parameters and default arguments: this allows a template with default
template arguments as an argument for a template template parameter
with fewer template parameters. This flag is enabled by default for
@option{-std=c++1z}.
@item -fno-nonansi-builtins
@opindex fno-nonansi-builtins
Disable built-in declarations of functions that are not mandated by

View File

@ -1,5 +1,5 @@
// { dg-do compile { target c++11 } }
// { dg-options -fno-new-ttp-matching }
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template<class... Types> class C { /* ... */ };

View File

@ -0,0 +1,24 @@
// CWG 150: Matching of template template-arguments excludes compatible
// templates
// { dg-options -fnew-ttp-matching }
template<class T> class A { /* ... */ };
template<class T, class U = T> class B { /* ... */ };
template<template<class> class P> class X { /* ... */ };
X<A> xa; // OK
X<B> xb; // OK since P0522R0
#if __cpp_variadic_templates
template <class ... Types> class C { /* ... */ };
template<template<class ...> class Q> class Y { /* ... */ };
X<C> xc; // OK since P0522R0
Y<A> ya; // OK
Y<B> yb; // OK
Y<C> yc; // OK
#endif
#if __cpp_template_auto
template<auto n> class D { /* ... */ };
template<template<int> class R> class Z { /* ... */ };
Z<D> zd; // OK
#endif

View File

@ -0,0 +1,17 @@
// { dg-do compile { target c++11 } }
template<typename _Tp>
struct get_first_arg;
template<template<typename, typename...> class _Template, typename _Tp,
typename... _Types>
struct get_first_arg<_Template<_Tp, _Types...>>
{ using type = _Tp; };
template<typename T> struct A { };
template<class,class> struct same;
template<class T> struct same<T,T> {};
same<get_first_arg<A<int>>::type,
int> x;

View File

@ -0,0 +1,10 @@
// { dg-do compile { target c++11 } }
template <typename, typename> struct A { };
template <typename T> struct B { };
template <typename T, template <T...> class C, T... Is>
struct A<B<T>, C<Is...>>
{
using type = C<Is...>;
};

View File

@ -1,5 +1,6 @@
// PR c++/33213
// { dg-do compile { target c++11 } }
// { dg-options -fno-new-ttp-matching }
template<template<typename> class...> struct A;

View File

@ -0,0 +1,7 @@
// PR c++/33213
// { dg-do compile { target c++11 } }
// { dg-options -fnew-ttp-matching }
template<template<typename> class...> struct A;
template<template<typename...> class... B> struct A<B...> {};

View File

@ -1,5 +1,6 @@
// PR c++/32565
// { dg-do compile { target c++11 } }
// { dg-options -fno-new-ttp-matching }
template<typename...> struct A1;
template<template<int, int...> class T> struct A1<T<0, 1> > {};

View File

@ -0,0 +1,10 @@
// PR c++/32565
// { dg-do compile { target c++11 } }
// { dg-options -fnew-ttp-matching }
template<typename...> struct A1;
template<template<int, int...> class T> struct A1<T<0, 1> > {};
template<int, int, int...> struct B1 {};
A1<B1<0, 1> > a1;
template<int...> struct B2 {};
A1<B2<0, 1> > a2;

View File

@ -0,0 +1,19 @@
// CWG 150: Matching of template template-arguments excludes compatible
// templates
// { dg-options -fnew-ttp-matching }
template<class T, class U = T> class B { /* ... */ };
#if __cpp_variadic_templates
template <class ... Types> class C { /* ... */ };
#endif
template<template<class> class P, class T> void f(P<T>);
int main()
{
f(B<int>());
f(B<int,float>()); // { dg-error "no match" }
#if __cpp_variadic_templates
f(C<int>());
#endif
}

View File

@ -1,3 +1,5 @@
// { dg-options -fno-new-ttp-matching }
template <template <typename> class C>
void f() {}

View File

@ -0,0 +1,9 @@
// { dg-options -fnew-ttp-matching }
template <template <typename> class C>
void f() {}
template <typename T, typename U = int>
struct S {};
template void f<S>();

View File

@ -1,3 +1,5 @@
// { dg-options -fno-new-ttp-matching }
template <template <typename> class C>
void f(C<double>) {}

View File

@ -0,0 +1,9 @@
// { dg-options -fnew-ttp-matching }
template <template <typename> class C>
void f(C<double>) {}
template <typename T, typename U = int>
struct S {};
template void f(S<double>);

View File

@ -1,3 +1,4 @@
// { dg-options -fno-new-ttp-matching }
// { dg-do compile }
namespace mpl {
template <typename, typename = int> struct lambda;

View File

@ -1,4 +1,4 @@
// { dg-do assemble }
// { dg-options -fno-new-ttp-matching }
template <int i> class C {};
template <template <long> class TT> class D {};

View File

@ -0,0 +1,9 @@
// { dg-options -fnew-ttp-matching }
template <int i> class C {};
template <template <long> class TT> class D {};
int main()
{
D<C> d;
}

View File

@ -1,4 +1,4 @@
2016-12-16 Jason Merrill <jason@redhat.com>
2016-12-21 Jason Merrill <jason@redhat.com>
* testsuite/util/testsuite_tr1.h (test_property): Don't define both
variadic and non-variadic overloads.