Allow partial specialization of variable templates.

* cp-tree.h (TINFO_USED_TEMPLATE_ID): New.
	* decl.c (duplicate_decls): Copy it.
	* error.c (dump_decl) [TEMPLATE_ID_EXPR]: Handle variables.
	* parser.c (cp_parser_decltype_expr): Do call finish_id_expression
	on template-ids.
	* pt.c (register_specialization): Remember variable template insts.
	(instantiate_template_1): Find the matching partial specialization.
	(check_explicit_specialization): Allow variable partial specialization.
	(process_partial_specialization): Likewise.
	(push_template_decl_real): Likewise.
	(more_specialized_partial_spec): Rename from more_specialized_class.
	(most_specialized_partial_spec): Rename from most_specialized_class.
	(get_partial_spec_bindings): Rename from get_class_bindings.

From-SVN: r218104
This commit is contained in:
Jason Merrill 2014-11-26 16:58:38 -05:00 committed by Jason Merrill
parent d896cc4d45
commit a2033ab107
10 changed files with 214 additions and 76 deletions

View File

@ -1,3 +1,20 @@
2014-11-26 Jason Merrill <jason@redhat.com>
Allow partial specialization of variable templates.
* cp-tree.h (TINFO_USED_TEMPLATE_ID): New.
* decl.c (duplicate_decls): Copy it.
* error.c (dump_decl) [TEMPLATE_ID_EXPR]: Handle variables.
* parser.c (cp_parser_decltype_expr): Do call finish_id_expression
on template-ids.
* pt.c (register_specialization): Remember variable template insts.
(instantiate_template_1): Find the matching partial specialization.
(check_explicit_specialization): Allow variable partial specialization.
(process_partial_specialization): Likewise.
(push_template_decl_real): Likewise.
(more_specialized_partial_spec): Rename from more_specialized_class.
(most_specialized_partial_spec): Rename from most_specialized_class.
(get_partial_spec_bindings): Rename from get_class_bindings.
2014-11-26 Paolo Carlini <paolo.carlini@oracle.com>
PR c++/63757

View File

@ -99,6 +99,7 @@ c-common.h, not after.
QUALIFIED_NAME_IS_TEMPLATE (in SCOPE_REF)
DECLTYPE_FOR_INIT_CAPTURE (in DECLTYPE_TYPE)
CONSTRUCTOR_NO_IMPLICIT_ZERO (in CONSTRUCTOR)
TINFO_USED_TEMPLATE_ID (in TEMPLATE_INFO)
2: IDENTIFIER_OPNAME_P (in IDENTIFIER_NODE)
ICS_THIS_FLAG (in _CONV)
DECL_INITIALIZED_BY_CONSTANT_EXPRESSION_P (in VAR_DECL)
@ -801,6 +802,12 @@ typedef struct qualified_typedef_usage_s qualified_typedef_usage_t;
#define FNDECL_HAS_ACCESS_ERRORS(NODE) \
(TINFO_HAS_ACCESS_ERRORS (DECL_TEMPLATE_INFO (NODE)))
/* Non-zero if this variable template specialization was specified using a
template-id, so it's a partial or full specialization and not a definition
of the member template of a particular class specialization. */
#define TINFO_USED_TEMPLATE_ID(NODE) \
(TREE_LANG_FLAG_1 (TEMPLATE_INFO_CHECK (NODE)))
struct GTY(()) tree_template_info {
struct tree_common common;
vec<qualified_typedef_usage_t, va_gc> *typedefs_needing_access_checking;

View File

@ -2137,7 +2137,14 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
DECL_LANG_SPECIFIC (newdecl)->u.min.u2 =
DECL_LANG_SPECIFIC (olddecl)->u.min.u2;
if (DECL_TEMPLATE_INFO (newdecl))
new_template_info = DECL_TEMPLATE_INFO (newdecl);
{
new_template_info = DECL_TEMPLATE_INFO (newdecl);
if (DECL_TEMPLATE_INSTANTIATION (olddecl)
&& DECL_TEMPLATE_SPECIALIZATION (newdecl))
/* Remember the presence of explicit specialization args. */
TINFO_USED_TEMPLATE_ID (DECL_TEMPLATE_INFO (olddecl))
= TINFO_USED_TEMPLATE_ID (new_template_info);
}
DECL_TEMPLATE_INFO (newdecl) = DECL_TEMPLATE_INFO (olddecl);
}
/* Only functions have these fields. */

View File

@ -1212,7 +1212,9 @@ dump_decl (cxx_pretty_printer *pp, tree t, int flags)
tree args = TREE_OPERAND (t, 1);
if (is_overloaded_fn (name))
name = DECL_NAME (get_first_fn (name));
name = get_first_fn (name);
if (DECL_P (name))
name = DECL_NAME (name);
dump_decl (pp, name, flags);
pp_cxx_begin_template_argument_list (pp);
if (args == error_mark_node)

View File

@ -12175,7 +12175,6 @@ cp_parser_decltype_expr (cp_parser *parser,
if (expr
&& expr != error_mark_node
&& TREE_CODE (expr) != TEMPLATE_ID_EXPR
&& TREE_CODE (expr) != TYPE_DECL
&& (TREE_CODE (expr) != BIT_NOT_EXPR
|| !TYPE_P (TREE_OPERAND (expr, 0)))

View File

@ -129,7 +129,7 @@ static int unify (tree, tree, tree, tree, int, bool);
static void add_pending_template (tree);
static tree reopen_tinst_level (struct tinst_level *);
static tree tsubst_initializer_list (tree, tree);
static tree get_class_bindings (tree, tree, tree, tree);
static tree get_partial_spec_bindings (tree, tree, tree, tree);
static tree coerce_template_parms (tree, tree, tree, tsubst_flags_t,
bool, bool);
static tree coerce_innermost_template_parms (tree, tree, tree, tsubst_flags_t,
@ -173,7 +173,7 @@ static tree tsubst_template_arg (tree, tree, tsubst_flags_t, tree);
static tree tsubst_template_args (tree, tree, tsubst_flags_t, tree);
static tree tsubst_template_parms (tree, tree, tsubst_flags_t);
static void regenerate_decl_from_template (tree, tree);
static tree most_specialized_class (tree, tsubst_flags_t);
static tree most_specialized_partial_spec (tree, tsubst_flags_t);
static tree tsubst_aggr_type (tree, tree, tsubst_flags_t, tree, int);
static tree tsubst_arg_types (tree, tree, tree, tsubst_flags_t, tree);
static tree tsubst_function_type (tree, tree, tsubst_flags_t, tree);
@ -1485,12 +1485,17 @@ register_specialization (tree spec, tree tmpl, tree args, bool is_friend,
gcc_assert (tmpl && args && spec);
*entry = elt;
*slot = entry;
if (TREE_CODE (spec) == FUNCTION_DECL && DECL_NAMESPACE_SCOPE_P (spec)
&& PRIMARY_TEMPLATE_P (tmpl)
&& DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (tmpl)) == NULL_TREE)
/* TMPL is a forward declaration of a template function; keep a list
if ((TREE_CODE (spec) == FUNCTION_DECL && DECL_NAMESPACE_SCOPE_P (spec)
&& PRIMARY_TEMPLATE_P (tmpl)
&& DECL_SAVED_TREE (DECL_TEMPLATE_RESULT (tmpl)) == NULL_TREE)
|| variable_template_p (tmpl))
/* If TMPL is a forward declaration of a template function, keep a list
of all specializations in case we need to reassign them to a friend
template later in tsubst_friend_function. */
template later in tsubst_friend_function.
Also keep a list of all variable template instantiations so that
process_partial_specialization can check whether a later partial
specialization would have used it. */
DECL_TEMPLATE_INSTANTIATIONS (tmpl)
= tree_cons (args, spec, DECL_TEMPLATE_INSTANTIATIONS (tmpl));
}
@ -2471,13 +2476,24 @@ check_explicit_specialization (tree declarator,
/* This case handles bogus declarations like template <>
template <class T> void f<int>(); */
if (uses_template_parms (declarator))
if (!uses_template_parms (declarator))
error ("template-id %qD in declaration of primary template",
declarator);
else if (variable_template_p (TREE_OPERAND (declarator, 0)))
{
/* Partial specialization of variable template. */
SET_DECL_TEMPLATE_SPECIALIZATION (decl);
specialization = 1;
goto ok;
}
else if (cxx_dialect < cxx14)
error ("non-type partial specialization %qD "
"is not allowed", declarator);
else
error ("template-id %qD in declaration of primary template",
declarator);
error ("non-class, non-variable partial specialization %qD "
"is not allowed", declarator);
return decl;
ok:;
}
if (ctype && CLASSTYPE_TEMPLATE_INSTANTIATION (ctype))
@ -2516,9 +2532,10 @@ check_explicit_specialization (tree declarator,
{
tree tmpl = NULL_TREE;
tree targs = NULL_TREE;
bool was_template_id = (TREE_CODE (declarator) == TEMPLATE_ID_EXPR);
/* Make sure that the declarator is a TEMPLATE_ID_EXPR. */
if (TREE_CODE (declarator) != TEMPLATE_ID_EXPR)
if (!was_template_id)
{
tree fns;
@ -2585,7 +2602,7 @@ check_explicit_specialization (tree declarator,
else if (ctype != NULL_TREE
&& (identifier_p (TREE_OPERAND (declarator, 0))))
{
// Ignore variable templates.
// We'll match variable templates in start_decl.
if (VAR_P (decl))
return decl;
@ -2722,7 +2739,7 @@ check_explicit_specialization (tree declarator,
/* If this is a specialization of a member template of a
template class, we want to return the TEMPLATE_DECL, not
the specialization of it. */
if (tsk == tsk_template)
if (tsk == tsk_template && !was_template_id)
{
tree result = DECL_TEMPLATE_RESULT (tmpl);
SET_DECL_TEMPLATE_SPECIALIZATION (tmpl);
@ -2747,6 +2764,9 @@ check_explicit_specialization (tree declarator,
/* Set up the DECL_TEMPLATE_INFO for DECL. */
DECL_TEMPLATE_INFO (decl) = build_template_info (tmpl, targs);
if (was_template_id)
TINFO_USED_TEMPLATE_ID (DECL_TEMPLATE_INFO (decl)) = true;
/* Inherit default function arguments from the template
DECL is specializing. */
if (DECL_FUNCTION_TEMPLATE_P (tmpl))
@ -4087,8 +4107,9 @@ static tree
process_partial_specialization (tree decl)
{
tree type = TREE_TYPE (decl);
tree maintmpl = CLASSTYPE_TI_TEMPLATE (type);
tree specargs = CLASSTYPE_TI_ARGS (type);
tree tinfo = get_template_info (decl);
tree maintmpl = TI_TEMPLATE (tinfo);
tree specargs = TI_ARGS (tinfo);
tree inner_args = INNERMOST_TEMPLATE_ARGS (specargs);
tree main_inner_parms = DECL_INNERMOST_TEMPLATE_PARMS (maintmpl);
tree inner_parms;
@ -4173,11 +4194,11 @@ process_partial_specialization (tree decl)
The argument list of the specialization shall not be identical to
the implicit argument list of the primary template. */
if (comp_template_args
(inner_args,
INNERMOST_TEMPLATE_ARGS (CLASSTYPE_TI_ARGS (TREE_TYPE
(maintmpl)))))
error ("partial specialization %qT does not specialize any template arguments", type);
tree main_args
= TI_ARGS (get_template_info (DECL_TEMPLATE_RESULT (maintmpl)));
if (comp_template_args (inner_args, INNERMOST_TEMPLATE_ARGS (main_args)))
error ("partial specialization %qD does not specialize "
"any template arguments", decl);
/* A partial specialization that replaces multiple parameters of the
primary template with a pack expansion is less specialized for those
@ -4317,7 +4338,8 @@ process_partial_specialization (tree decl)
}
/* We should only get here once. */
gcc_assert (!COMPLETE_TYPE_P (type));
if (TREE_CODE (decl) == TYPE_DECL)
gcc_assert (!COMPLETE_TYPE_P (type));
tree tmpl = build_template_decl (decl, current_template_parms,
DECL_MEMBER_TEMPLATE_P (maintmpl));
@ -4335,15 +4357,21 @@ process_partial_specialization (tree decl)
for (inst = DECL_TEMPLATE_INSTANTIATIONS (maintmpl); inst;
inst = TREE_CHAIN (inst))
{
tree inst_type = TREE_VALUE (inst);
if (COMPLETE_TYPE_P (inst_type)
&& CLASSTYPE_IMPLICIT_INSTANTIATION (inst_type))
tree instance = TREE_VALUE (inst);
if (TYPE_P (instance)
? (COMPLETE_TYPE_P (instance)
&& CLASSTYPE_IMPLICIT_INSTANTIATION (instance))
: DECL_TEMPLATE_INSTANTIATION (instance))
{
tree spec = most_specialized_class (inst_type, tf_none);
if (spec && TREE_TYPE (spec) == type)
permerror (input_location,
"partial specialization of %qT after instantiation "
"of %qT", type, inst_type);
tree spec = most_specialized_partial_spec (instance, tf_none);
if (spec && TREE_VALUE (spec) == tmpl)
{
tree inst_decl = (DECL_P (instance)
? instance : TYPE_NAME (instance));
permerror (input_location,
"partial specialization of %qD after instantiation "
"of %qD", decl, inst_decl);
}
}
}
@ -4692,9 +4720,13 @@ push_template_decl_real (tree decl, bool is_friend)
return error_mark_node;
/* See if this is a partial specialization. */
is_partial = (DECL_IMPLICIT_TYPEDEF_P (decl)
&& TREE_CODE (TREE_TYPE (decl)) != ENUMERAL_TYPE
&& CLASSTYPE_TEMPLATE_SPECIALIZATION (TREE_TYPE (decl)));
is_partial = ((DECL_IMPLICIT_TYPEDEF_P (decl)
&& TREE_CODE (TREE_TYPE (decl)) != ENUMERAL_TYPE
&& CLASSTYPE_TEMPLATE_SPECIALIZATION (TREE_TYPE (decl)))
|| (TREE_CODE (decl) == VAR_DECL
&& DECL_LANG_SPECIFIC (decl)
&& DECL_TEMPLATE_SPECIALIZATION (decl)
&& TINFO_USED_TEMPLATE_ID (DECL_TEMPLATE_INFO (decl))));
if (TREE_CODE (decl) == FUNCTION_DECL && DECL_FRIEND_P (decl))
is_friend = true;
@ -9057,7 +9089,7 @@ instantiate_class_template_1 (tree type)
/* Determine what specialization of the original template to
instantiate. */
t = most_specialized_class (type, tf_warning_or_error);
t = most_specialized_partial_spec (type, tf_warning_or_error);
if (t == error_mark_node)
{
TYPE_BEING_DEFINED (type) = 1;
@ -10519,7 +10551,7 @@ tsubst_decl (tree t, tree args, tsubst_flags_t complain)
if (new_type == error_mark_node)
RETURN (error_mark_node);
/* If we get a real template back, return it. This can happen in
the context of most_specialized_class. */
the context of most_specialized_partial_spec. */
if (TREE_CODE (new_type) == TEMPLATE_DECL)
return new_type;
@ -15878,9 +15910,28 @@ instantiate_template_1 (tree tmpl, tree orig_args, tsubst_flags_t complain)
complain, gen_tmpl, true);
push_nested_class (ctx);
}
tree pattern = DECL_TEMPLATE_RESULT (gen_tmpl);
if (VAR_P (pattern))
{
/* We need to determine if we're using a partial or explicit
specialization now, because the type of the variable could be
different. */
tree tid = lookup_template_variable (gen_tmpl, targ_ptr);
tree elt = most_specialized_partial_spec (tid, complain);
if (elt == error_mark_node)
pattern = error_mark_node;
else if (elt)
{
tmpl = TREE_VALUE (elt);
pattern = DECL_TEMPLATE_RESULT (tmpl);
targ_ptr = TREE_PURPOSE (elt);
}
}
/* Substitute template parameters to obtain the specialization. */
fndecl = tsubst (DECL_TEMPLATE_RESULT (gen_tmpl),
targ_ptr, complain, gen_tmpl);
fndecl = tsubst (pattern, targ_ptr, complain, gen_tmpl);
if (DECL_CLASS_SCOPE_P (gen_tmpl))
pop_nested_class ();
pop_from_top_level ();
@ -18881,8 +18932,8 @@ more_specialized_fn (tree pat1, tree pat2, int len)
/* Determine which of two partial specializations of TMPL is more
specialized.
PAT1 is a TREE_LIST whose TREE_TYPE is the _TYPE node corresponding
to the first partial specialization. The TREE_VALUE is the
PAT1 is a TREE_LIST whose TREE_VALUE is the TEMPLATE_DECL corresponding
to the first partial specialization. The TREE_PURPOSE is the
innermost set of template parameters for the partial
specialization. PAT2 is similar, but for the second template.
@ -18894,33 +18945,32 @@ more_specialized_fn (tree pat1, tree pat2, int len)
two templates is more specialized. */
static int
more_specialized_class (tree tmpl, tree pat1, tree pat2)
more_specialized_partial_spec (tree tmpl, tree pat1, tree pat2)
{
tree targs;
tree tmpl1, tmpl2;
int winner = 0;
bool any_deductions = false;
tmpl1 = TREE_TYPE (pat1);
tmpl2 = TREE_TYPE (pat2);
tree tmpl1 = TREE_VALUE (pat1);
tree tmpl2 = TREE_VALUE (pat2);
tree parms1 = DECL_INNERMOST_TEMPLATE_PARMS (tmpl1);
tree parms2 = DECL_INNERMOST_TEMPLATE_PARMS (tmpl2);
tree specargs1 = TI_ARGS (get_template_info (DECL_TEMPLATE_RESULT (tmpl1)));
tree specargs2 = TI_ARGS (get_template_info (DECL_TEMPLATE_RESULT (tmpl2)));
/* Just like what happens for functions, if we are ordering between
different class template specializations, we may encounter dependent
different template specializations, we may encounter dependent
types in the arguments, and we need our dependency check functions
to behave correctly. */
++processing_template_decl;
targs = get_class_bindings (tmpl, TREE_VALUE (pat1),
CLASSTYPE_TI_ARGS (tmpl1),
CLASSTYPE_TI_ARGS (tmpl2));
targs = get_partial_spec_bindings (tmpl, parms1, specargs1, specargs2);
if (targs)
{
--winner;
any_deductions = true;
}
targs = get_class_bindings (tmpl, TREE_VALUE (pat2),
CLASSTYPE_TI_ARGS (tmpl2),
CLASSTYPE_TI_ARGS (tmpl1));
targs = get_partial_spec_bindings (tmpl, parms2, specargs2, specargs1);
if (targs)
{
++winner;
@ -18928,7 +18978,7 @@ more_specialized_class (tree tmpl, tree pat1, tree pat2)
}
--processing_template_decl;
/* In the case of a tie where at least one of the class templates
/* In the case of a tie where at least one of the templates
has a parameter pack at the end, the template with the most
non-packed parameters wins. */
if (winner == 0
@ -19014,7 +19064,7 @@ get_bindings (tree fn, tree decl, tree explicit_args, bool check_rettype)
is bound to `double'. */
static tree
get_class_bindings (tree tmpl, tree tparms, tree spec_args, tree args)
get_partial_spec_bindings (tree tmpl, tree tparms, tree spec_args, tree args)
{
int i, ntparms = TREE_VEC_LENGTH (tparms);
tree deduced_args;
@ -19197,20 +19247,20 @@ most_general_template (tree decl)
return decl;
}
/* Return the most specialized of the class template partial
specializations which can produce TYPE, a specialization of some class
template. The value returned is actually a TREE_LIST; the TREE_TYPE is
a _TYPE node corresponding to the partial specialization, while the
TREE_PURPOSE is the set of template arguments that must be
substituted into the TREE_TYPE in order to generate TYPE.
/* Return the most specialized of the template partial specializations
which can produce TARGET, a specialization of some class or variable
template. The value returned is actually a TREE_LIST; the TREE_VALUE is
a TEMPLATE_DECL node corresponding to the partial specialization, while
the TREE_PURPOSE is the set of template arguments that must be
substituted into the template pattern in order to generate TARGET.
If the choice of partial specialization is ambiguous, a diagnostic
is issued, and the error_mark_node is returned. If there are no
partial specializations matching TYPE, then NULL_TREE is
partial specializations matching TARGET, then NULL_TREE is
returned, indicating that the primary template should be used. */
static tree
most_specialized_class (tree type, tsubst_flags_t complain)
most_specialized_partial_spec (tree target, tsubst_flags_t complain)
{
tree list = NULL_TREE;
tree t;
@ -19218,10 +19268,29 @@ most_specialized_class (tree type, tsubst_flags_t complain)
int fate;
bool ambiguous_p;
tree outer_args = NULL_TREE;
tree tmpl, args;
if (TYPE_P (target))
{
tree tinfo = CLASSTYPE_TEMPLATE_INFO (target);
tmpl = TI_TEMPLATE (tinfo);
args = TI_ARGS (tinfo);
}
else if (TREE_CODE (target) == TEMPLATE_ID_EXPR)
{
tmpl = TREE_OPERAND (target, 0);
args = TREE_OPERAND (target, 1);
}
else if (VAR_P (target))
{
tree tinfo = DECL_TEMPLATE_INFO (target);
tmpl = TI_TEMPLATE (tinfo);
args = TI_ARGS (tinfo);
}
else
gcc_unreachable ();
tree tmpl = CLASSTYPE_TI_TEMPLATE (type);
tree main_tmpl = most_general_template (tmpl);
tree args = CLASSTYPE_TI_ARGS (type);
/* For determining which partial specialization to use, only the
innermost args are interesting. */
@ -19236,9 +19305,8 @@ most_specialized_class (tree type, tsubst_flags_t complain)
tree partial_spec_args;
tree spec_args;
tree spec_tmpl = TREE_VALUE (t);
tree orig_parms = DECL_INNERMOST_TEMPLATE_PARMS (spec_tmpl);
partial_spec_args = CLASSTYPE_TI_ARGS (TREE_TYPE (t));
partial_spec_args = TREE_PURPOSE (t);
++processing_template_decl;
@ -19269,14 +19337,14 @@ most_specialized_class (tree type, tsubst_flags_t complain)
return error_mark_node;
tree parms = DECL_INNERMOST_TEMPLATE_PARMS (spec_tmpl);
spec_args = get_class_bindings (tmpl, parms,
spec_args = get_partial_spec_bindings (tmpl, parms,
partial_spec_args,
args);
if (spec_args)
{
if (outer_args)
spec_args = add_to_template_args (outer_args, spec_args);
list = tree_cons (spec_args, orig_parms, list);
list = tree_cons (spec_args, TREE_VALUE (t), list);
TREE_TYPE (list) = TREE_TYPE (t);
}
}
@ -19290,7 +19358,7 @@ most_specialized_class (tree type, tsubst_flags_t complain)
t = TREE_CHAIN (t);
for (; t; t = TREE_CHAIN (t))
{
fate = more_specialized_class (tmpl, champ, t);
fate = more_specialized_partial_spec (tmpl, champ, t);
if (fate == 1)
;
else
@ -19311,7 +19379,7 @@ most_specialized_class (tree type, tsubst_flags_t complain)
if (!ambiguous_p)
for (t = list; t && t != champ; t = TREE_CHAIN (t))
{
fate = more_specialized_class (tmpl, champ, t);
fate = more_specialized_partial_spec (tmpl, champ, t);
if (fate != 1)
{
ambiguous_p = true;
@ -19325,11 +19393,16 @@ most_specialized_class (tree type, tsubst_flags_t complain)
char *spaces = NULL;
if (!(complain & tf_error))
return error_mark_node;
error ("ambiguous class template instantiation for %q#T", type);
if (TYPE_P (target))
error ("ambiguous template instantiation for %q#T", target);
else
error ("ambiguous template instantiation for %q#D", target);
str = ngettext ("candidate is:", "candidates are:", list_length (list));
for (t = list; t; t = TREE_CHAIN (t))
{
error ("%s %+#T", spaces ? spaces : str, TREE_TYPE (t));
tree subst = build_tree_list (TREE_VALUE (t), TREE_PURPOSE (t));
inform (DECL_SOURCE_LOCATION (TREE_VALUE (t)),
"%s %#S", spaces ? spaces : str, subst);
spaces = spaces ? spaces : get_spaces (str);
}
free (spaces);

View File

@ -13,25 +13,25 @@ struct metatuple<add_pointer> {
};
template<template<class T> class Meta>
struct metatuple<Meta, Meta> { // { dg-error "candidates" }
struct metatuple<Meta, Meta> { // { dg-message "candidates" }
static const int value = 2;
};
template<template<class T> class... Metafunctions>
struct metatuple<add_pointer, Metafunctions...> { // { dg-error "" }
struct metatuple<add_pointer, Metafunctions...> { // { dg-message "" }
static const int value = 3;
};
template<template<class T> class First,
template<class T> class... Metafunctions>
struct metatuple<First, Metafunctions...> { // { dg-error "struct" }
struct metatuple<First, Metafunctions...> { // { dg-message "struct" }
static const int value = 4;
};
template<template<class T> class First,
template<class T> class Second,
template<class T> class... Metafunctions>
struct metatuple<First, Second, Metafunctions...> { // { dg-error "struct" }
struct metatuple<First, Second, Metafunctions...> { // { dg-message "struct" }
static const int value = 5;
};

View File

@ -0,0 +1,6 @@
// { dg-do compile { target c++14 } }
template <class T> T t = 42;
template <class T> T* t<T*> = nullptr;
void *p = t<void*>;

View File

@ -0,0 +1,9 @@
// DR 1727: a specialization doesn't need to have the same type
// { dg-do compile { target c++14 } }
template <class T> T t = 42;
template <class T> int t<T*> = 0;
template<class T, class U> struct same;
template<class T> struct same<T,T> {};
same<int,decltype(t<void*>)> s;

View File

@ -0,0 +1,18 @@
// { dg-do compile { target c++14 } }
template <class T> T t1 = 42;
template <class T> T* t1<T> = nullptr; // { dg-error "partial" }
template <class T> T t2 = 42;
template <class T> T* t2<T*> = nullptr;
template <class T> T* t2<T*> = nullptr; // { dg-error "redefinition" }
template <class T, class U> T t3 = U();
template <class T> T t3<T,int> = 42;
template <class U> int t3<int,U> = U();
int i = t3<int,int>; // { dg-error "ambiguous" }
template <class T> T t4 = T();
void *p = t4<void*>;
template <class T> T* t4<T*> = nullptr; // { dg-error "after instantiation" }