Commit 853ef4e5 by Jason Merrill Committed by Jason Merrill

Class template argument deduction refinements

	* call.c (joust): Move deduction guide tiebreaker down.
	* decl.c (start_decl_1, cp_finish_decl, grokdeclarator): Allow class
	deduction with no initializer.
	* pt.c (build_deduction_guide): Handle implicit default/copy ctor.
	(do_class_deduction): Use that rather than special case.
	(do_auto_deduction): Handle null initializer.

From-SVN: r245796
parent ad1de652
2017-02-28 Jason Merrill <jason@redhat.com>
Class template argument deduction refinements
* call.c (joust): Move deduction guide tiebreaker down.
* decl.c (start_decl_1, cp_finish_decl, grokdeclarator): Allow class
deduction with no initializer.
* pt.c (build_deduction_guide): Handle implicit default/copy ctor.
(do_class_deduction): Use that rather than special case.
(do_auto_deduction): Handle null initializer.
2017-02-28 Jakub Jelinek <jakub@redhat.com> 2017-02-28 Jakub Jelinek <jakub@redhat.com>
* decl.c (find_decomp_class_base): Use cond ? G_("...") : G_("...") * decl.c (find_decomp_class_base): Use cond ? G_("...") : G_("...")
......
...@@ -9670,18 +9670,6 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, ...@@ -9670,18 +9670,6 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
return winner; return winner;
} }
/* F1 is generated from a deduction-guide (13.3.1.8) and F2 is not */
if (deduction_guide_p (cand1->fn))
{
gcc_assert (deduction_guide_p (cand2->fn));
/* We distinguish between candidates from an explicit deduction guide and
candidates built from a constructor based on DECL_ARTIFICIAL. */
int art1 = DECL_ARTIFICIAL (cand1->fn);
int art2 = DECL_ARTIFICIAL (cand2->fn);
if (art1 != art2)
return art2 - art1;
}
/* or, if not that, /* or, if not that,
F1 is a non-template function and F2 is a template function F1 is a non-template function and F2 is a template function
specialization. */ specialization. */
...@@ -9719,6 +9707,18 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, ...@@ -9719,6 +9707,18 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
return winner; return winner;
} }
/* F1 is generated from a deduction-guide (13.3.1.8) and F2 is not */
if (deduction_guide_p (cand1->fn))
{
gcc_assert (deduction_guide_p (cand2->fn));
/* We distinguish between candidates from an explicit deduction guide and
candidates built from a constructor based on DECL_ARTIFICIAL. */
int art1 = DECL_ARTIFICIAL (cand1->fn);
int art2 = DECL_ARTIFICIAL (cand2->fn);
if (art1 != art2)
return art2 - art1;
}
/* or, if not that, F2 is from a using-declaration, F1 is not, and the /* or, if not that, F2 is from a using-declaration, F1 is not, and the
conversion sequences are equivalent. conversion sequences are equivalent.
(proposed in http://lists.isocpp.org/core/2016/10/1142.php) */ (proposed in http://lists.isocpp.org/core/2016/10/1142.php) */
......
...@@ -5238,14 +5238,16 @@ start_decl_1 (tree decl, bool initialized) ...@@ -5238,14 +5238,16 @@ start_decl_1 (tree decl, bool initialized)
else if (aggregate_definition_p && !complete_p) else if (aggregate_definition_p && !complete_p)
{ {
if (type_uses_auto (type)) if (type_uses_auto (type))
gcc_unreachable (); gcc_assert (CLASS_PLACEHOLDER_TEMPLATE (type));
else else
{
error ("aggregate %q#D has incomplete type and cannot be defined", error ("aggregate %q#D has incomplete type and cannot be defined",
decl); decl);
/* Change the type so that assemble_variable will give /* Change the type so that assemble_variable will give
DECL an rtl we can live with: (mem (const_int 0)). */ DECL an rtl we can live with: (mem (const_int 0)). */
type = TREE_TYPE (decl) = error_mark_node; type = TREE_TYPE (decl) = error_mark_node;
} }
}
/* Create a new scope to hold this declaration if necessary. /* Create a new scope to hold this declaration if necessary.
Whether or not a new scope is necessary cannot be determined Whether or not a new scope is necessary cannot be determined
...@@ -6816,14 +6818,17 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, ...@@ -6816,14 +6818,17 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p,
return; return;
} }
gcc_unreachable (); gcc_assert (CLASS_PLACEHOLDER_TEMPLATE (auto_node));
} }
d_init = init; d_init = init;
if (d_init)
{
if (TREE_CODE (d_init) == TREE_LIST if (TREE_CODE (d_init) == TREE_LIST
&& !CLASS_PLACEHOLDER_TEMPLATE (auto_node)) && !CLASS_PLACEHOLDER_TEMPLATE (auto_node))
d_init = build_x_compound_expr_from_list (d_init, ELK_INIT, d_init = build_x_compound_expr_from_list (d_init, ELK_INIT,
tf_warning_or_error); tf_warning_or_error);
d_init = resolve_nondeduced_context (d_init, tf_warning_or_error); d_init = resolve_nondeduced_context (d_init, tf_warning_or_error);
}
enum auto_deduction_context adc = adc_variable_type; enum auto_deduction_context adc = adc_variable_type;
if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl)) if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl))
adc = adc_decomp_type; adc = adc_decomp_type;
...@@ -12323,16 +12328,9 @@ grokdeclarator (const cp_declarator *declarator, ...@@ -12323,16 +12328,9 @@ grokdeclarator (const cp_declarator *declarator,
if (VAR_P (decl) && !initialized) if (VAR_P (decl) && !initialized)
if (tree auto_node = type_uses_auto (type)) if (tree auto_node = type_uses_auto (type))
if (!CLASS_PLACEHOLDER_TEMPLATE (auto_node))
{ {
location_t loc = declspecs->locations[ds_type_spec]; location_t loc = declspecs->locations[ds_type_spec];
if (tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (auto_node))
{
error_at (loc, "invalid use of template-name %qE without an "
"argument list", tmpl);
inform (loc, "class template argument deduction "
"requires an initializer");
}
else
error_at (loc, "declaration of %q#D has no initializer", decl); error_at (loc, "declaration of %q#D has no initializer", decl);
TREE_TYPE (decl) = error_mark_node; TREE_TYPE (decl) = error_mark_node;
} }
......
...@@ -24941,14 +24941,42 @@ rewrite_template_parm (tree olddecl, unsigned index, unsigned level, ...@@ -24941,14 +24941,42 @@ rewrite_template_parm (tree olddecl, unsigned index, unsigned level,
} }
/* Returns a C++17 class deduction guide template based on the constructor /* Returns a C++17 class deduction guide template based on the constructor
CTOR. */ CTOR. As a special case, CTOR can be a RECORD_TYPE for an implicit default
guide, or REFERENCE_TYPE for an implicit copy/move guide. */
static tree static tree
build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain) build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
{ {
tree type, tparms, targs, fparms, fargs, ci;
bool memtmpl = false;
bool explicit_p;
location_t loc;
if (TYPE_P (ctor))
{
type = ctor;
bool copy_p = TREE_CODE (type) == REFERENCE_TYPE;
if (copy_p)
{
type = TREE_TYPE (type);
fparms = tree_cons (NULL_TREE, type, void_list_node);
}
else
fparms = void_list_node;
tree ctmpl = CLASSTYPE_TI_TEMPLATE (type);
tparms = DECL_TEMPLATE_PARMS (ctmpl);
targs = CLASSTYPE_TI_ARGS (type);
ci = NULL_TREE;
fargs = NULL_TREE;
loc = DECL_SOURCE_LOCATION (ctmpl);
explicit_p = false;
}
else
{
if (outer_args) if (outer_args)
ctor = tsubst (ctor, outer_args, complain, ctor); ctor = tsubst (ctor, outer_args, complain, ctor);
tree type = DECL_CONTEXT (ctor); type = DECL_CONTEXT (ctor);
tree fn_tmpl; tree fn_tmpl;
if (TREE_CODE (ctor) == TEMPLATE_DECL) if (TREE_CODE (ctor) == TEMPLATE_DECL)
{ {
...@@ -24958,34 +24986,38 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain) ...@@ -24958,34 +24986,38 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
else else
fn_tmpl = DECL_TI_TEMPLATE (ctor); fn_tmpl = DECL_TI_TEMPLATE (ctor);
tree tparms = DECL_TEMPLATE_PARMS (fn_tmpl); tparms = DECL_TEMPLATE_PARMS (fn_tmpl);
/* If type is a member class template, DECL_TI_ARGS (ctor) will have fully /* If type is a member class template, DECL_TI_ARGS (ctor) will have
specialized args for the enclosing class. Strip those off, as the fully specialized args for the enclosing class. Strip those off, as
deduction guide won't have those template parameters. */ the deduction guide won't have those template parameters. */
tree targs = get_innermost_template_args (DECL_TI_ARGS (ctor), targs = get_innermost_template_args (DECL_TI_ARGS (ctor),
TMPL_PARMS_DEPTH (tparms)); TMPL_PARMS_DEPTH (tparms));
/* Discard the 'this' parameter. */ /* Discard the 'this' parameter. */
tree fparms = FUNCTION_ARG_CHAIN (ctor); fparms = FUNCTION_ARG_CHAIN (ctor);
tree fargs = TREE_CHAIN (DECL_ARGUMENTS (ctor)); fargs = TREE_CHAIN (DECL_ARGUMENTS (ctor));
tree ci = get_constraints (ctor); ci = get_constraints (ctor);
loc = DECL_SOURCE_LOCATION (ctor);
explicit_p = DECL_NONCONVERTING_P (ctor);
if (PRIMARY_TEMPLATE_P (fn_tmpl)) if (PRIMARY_TEMPLATE_P (fn_tmpl))
{ {
/* For a member template constructor, we need to flatten the two template memtmpl = true;
parameter lists into one, and then adjust the function signature
accordingly. This gets...complicated. */ /* For a member template constructor, we need to flatten the two
template parameter lists into one, and then adjust the function
signature accordingly. This gets...complicated. */
++processing_template_decl; ++processing_template_decl;
tree save_parms = current_template_parms; tree save_parms = current_template_parms;
/* For a member template we should have two levels of parms/args, one for /* For a member template we should have two levels of parms/args, one
the class and one for the constructor. We stripped specialized args for the class and one for the constructor. We stripped
for further enclosing classes above. */ specialized args for further enclosing classes above. */
const int depth = 2; const int depth = 2;
gcc_assert (TMPL_ARGS_DEPTH (targs) == depth); gcc_assert (TMPL_ARGS_DEPTH (targs) == depth);
/* Template args for translating references to the two-level template /* Template args for translating references to the two-level template
parameters into references to the one-level template parameters we are parameters into references to the one-level template parameters we
creating. */ are creating. */
tree tsubst_args = copy_node (targs); tree tsubst_args = copy_node (targs);
TMPL_ARGS_LEVEL (tsubst_args, depth) TMPL_ARGS_LEVEL (tsubst_args, depth)
= copy_node (TMPL_ARGS_LEVEL (tsubst_args, depth)); = copy_node (TMPL_ARGS_LEVEL (tsubst_args, depth));
...@@ -24997,8 +25029,8 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain) ...@@ -24997,8 +25029,8 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
tparms = TREE_CHAIN (tparms); tparms = TREE_CHAIN (tparms);
tree ctparms = TREE_VALUE (tparms); tree ctparms = TREE_VALUE (tparms);
unsigned clen = TREE_VEC_LENGTH (ctparms); unsigned clen = TREE_VEC_LENGTH (ctparms);
/* Template parms for the deduction guide start as a copy of the template /* Template parms for the deduction guide start as a copy of the
parms for the class. We set current_template_parms for template parms for the class. We set current_template_parms for
lookup_template_class_1. */ lookup_template_class_1. */
current_template_parms = tparms = copy_node (tparms); current_template_parms = tparms = copy_node (tparms);
tree new_vec = TREE_VALUE (tparms) = make_tree_vec (flen + clen); tree new_vec = TREE_VALUE (tparms) = make_tree_vec (flen + clen);
...@@ -25037,7 +25069,9 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain) ...@@ -25037,7 +25069,9 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
current_template_parms = save_parms; current_template_parms = save_parms;
--processing_template_decl; --processing_template_decl;
} }
else }
if (!memtmpl)
{ {
/* Copy the parms so we can set DECL_PRIMARY_TEMPLATE. */ /* Copy the parms so we can set DECL_PRIMARY_TEMPLATE. */
tparms = copy_node (tparms); tparms = copy_node (tparms);
...@@ -25046,12 +25080,12 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain) ...@@ -25046,12 +25080,12 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
} }
tree fntype = build_function_type (type, fparms); tree fntype = build_function_type (type, fparms);
tree ded_fn = build_lang_decl_loc (DECL_SOURCE_LOCATION (ctor), tree ded_fn = build_lang_decl_loc (loc,
FUNCTION_DECL, FUNCTION_DECL,
dguide_name (type), fntype); dguide_name (type), fntype);
DECL_ARGUMENTS (ded_fn) = fargs; DECL_ARGUMENTS (ded_fn) = fargs;
DECL_ARTIFICIAL (ded_fn) = true; DECL_ARTIFICIAL (ded_fn) = true;
DECL_NONCONVERTING_P (ded_fn) = DECL_NONCONVERTING_P (ctor); DECL_NONCONVERTING_P (ded_fn) = explicit_p;
tree ded_tmpl = build_template_decl (ded_fn, tparms, /*member*/false); tree ded_tmpl = build_template_decl (ded_fn, tparms, /*member*/false);
DECL_ARTIFICIAL (ded_tmpl) = true; DECL_ARTIFICIAL (ded_tmpl) = true;
DECL_TEMPLATE_RESULT (ded_tmpl) = ded_fn; DECL_TEMPLATE_RESULT (ded_tmpl) = ded_fn;
...@@ -25085,27 +25119,16 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, ...@@ -25085,27 +25119,16 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
tree type = TREE_TYPE (tmpl); tree type = TREE_TYPE (tmpl);
vec<tree,va_gc> *args; vec<tree,va_gc> *args;
if (TREE_CODE (init) == TREE_LIST) if (init == NULL_TREE
|| TREE_CODE (init) == TREE_LIST)
args = make_tree_vector_from_list (init); args = make_tree_vector_from_list (init);
else if (BRACE_ENCLOSED_INITIALIZER_P (init)) else if (BRACE_ENCLOSED_INITIALIZER_P (init)
&& !TYPE_HAS_LIST_CTOR (type)
&& !is_std_init_list (type))
args = make_tree_vector_from_ctor (init); args = make_tree_vector_from_ctor (init);
else else
args = make_tree_vector_single (init); args = make_tree_vector_single (init);
if (args->length() == 1)
{
/* First try to deduce directly, since we don't have implicitly-declared
constructors yet. */
tree parms = build_tree_list (NULL_TREE, type);
tree tparms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl));
tree targs = make_tree_vec (TREE_VEC_LENGTH (tparms));
int err = type_unification_real (tparms, targs, parms, &(*args)[0],
1, /*subr*/false, DEDUCE_CALL,
LOOKUP_NORMAL, NULL, /*explain*/false);
if (err == 0)
return tsubst (type, targs, complain, tmpl);
}
tree dname = dguide_name (tmpl); tree dname = dguide_name (tmpl);
tree cands = lookup_qualified_name (CP_DECL_CONTEXT (tmpl), dname, tree cands = lookup_qualified_name (CP_DECL_CONTEXT (tmpl), dname,
/*type*/false, /*complain*/false, /*type*/false, /*complain*/false,
...@@ -25121,6 +25144,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, ...@@ -25121,6 +25144,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
type = TREE_TYPE (most_general_template (tmpl)); type = TREE_TYPE (most_general_template (tmpl));
} }
bool saw_default = false;
bool saw_copy = false;
if (CLASSTYPE_METHOD_VEC (type)) if (CLASSTYPE_METHOD_VEC (type))
// FIXME cache artificial deduction guides // FIXME cache artificial deduction guides
for (tree fns = CLASSTYPE_CONSTRUCTORS (type); fns; fns = OVL_NEXT (fns)) for (tree fns = CLASSTYPE_CONSTRUCTORS (type); fns; fns = OVL_NEXT (fns))
...@@ -25128,21 +25153,30 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, ...@@ -25128,21 +25153,30 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
tree fn = OVL_CURRENT (fns); tree fn = OVL_CURRENT (fns);
tree guide = build_deduction_guide (fn, outer_args, complain); tree guide = build_deduction_guide (fn, outer_args, complain);
cands = ovl_cons (guide, cands); cands = ovl_cons (guide, cands);
}
if (cands == NULL_TREE) tree parms = FUNCTION_FIRST_USER_PARMTYPE (fn);
if (sufficient_parms_p (parms))
saw_default = true;
if (parms && sufficient_parms_p (TREE_CHAIN (parms)))
{ {
if (args->length() == 0) tree pt = TREE_VALUE (parms);
if (TREE_CODE (pt) == REFERENCE_TYPE
&& (same_type_ignoring_top_level_qualifiers_p
(TREE_TYPE (pt), type)))
saw_copy = true;
}
}
if (!saw_default && args->length() == 0)
{ {
/* Try tmpl<>. */ tree guide = build_deduction_guide (type, outer_args, complain);
tree t = lookup_template_class (tmpl, NULL_TREE, NULL_TREE, cands = ovl_cons (guide, cands);
NULL_TREE, false, tf_none);
if (t != error_mark_node)
return t;
} }
error ("cannot deduce template arguments for %qT, as it has " if (!saw_copy && args->length() == 1)
"no deduction guides or user-declared constructors", type); {
return error_mark_node; tree guide = build_deduction_guide (build_reference_type (type),
outer_args, complain);
cands = ovl_cons (guide, cands);
} }
/* Prune explicit deduction guides in copy-initialization context. */ /* Prune explicit deduction guides in copy-initialization context. */
...@@ -25225,7 +25259,7 @@ do_auto_deduction (tree type, tree init, tree auto_node, ...@@ -25225,7 +25259,7 @@ do_auto_deduction (tree type, tree init, tree auto_node,
if (init == error_mark_node) if (init == error_mark_node)
return error_mark_node; return error_mark_node;
if (type_dependent_expression_p (init) if (init && type_dependent_expression_p (init)
&& context != adc_unify) && context != adc_unify)
/* Defining a subset of type-dependent expressions that we can deduce /* Defining a subset of type-dependent expressions that we can deduce
from ahead of time isn't worth the trouble. */ from ahead of time isn't worth the trouble. */
......
// { dg-options -std=c++1z }
#include <initializer_list>
template <class T>
struct A
{
A (std::initializer_list<T>);
};
A a{1,2};
...@@ -15,10 +15,10 @@ template<class T> struct A { ...@@ -15,10 +15,10 @@ template<class T> struct A {
template<class T, int N = remove_ref_t<T>::value> A(T&&, int*) -> A<T>; //#3 template<class T, int N = remove_ref_t<T>::value> A(T&&, int*) -> A<T>; //#3
A a{1,0}; // uses #1 to deduce A<int> and initializes with #1 A a{1,0}; // uses #1 to deduce A<int> and initializes with #1
A b{a,0}; // uses #3 (not #2) to deduce A<A<int>&> and initializes with #1 A b{a,0}; // uses #2 to deduce A<int> and initializes with #2
template <class,class> struct same; template <class,class> struct same;
template <class T> struct same<T,T> {}; template <class T> struct same<T,T> {};
same<decltype(a),A<int>> s1; same<decltype(a),A<int>> s1;
same<decltype(b),A<A<int>&>> s2; same<decltype(b),A<int>> s2;
...@@ -3,4 +3,4 @@ ...@@ -3,4 +3,4 @@
template <class T = void> struct A { }; template <class T = void> struct A { };
A a{}; A a{};
A a2;
// { dg-options -std=c++1z }
template <class T> struct A {
A(T); // #1
A(const A&); // #2
};
template <class T> A(T) -> A<T>; // #3
A a (42); // uses #3 to deduce A<int> and initializes with #1
A b = a; // uses #2 (not #3) to deduce A<int> and initializes with #2; #2 is more specialized
template <class T> A(A<T>) -> A<A<T>>; // #4
A b2 = a; // uses #4 to deduce A<A<int>> and initializes with #1; #4 is as specialized as #2
template <class,class> struct same;
template <class T> struct same<T,T> {};
same<decltype(a),A<int>> s1;
same<decltype(b),A<int>> s2;
same<decltype(b2),A<A<int>>> s3;
// { dg-options -std=c++1z }
#include <initializer_list>
std::initializer_list l { 1, 2, 3 };
...@@ -10,12 +10,13 @@ namespace N ...@@ -10,12 +10,13 @@ namespace N
int K; int K;
} }
N::A f2; // { dg-error "1:invalid use of template-name 'N::A' without an argument list" } N::A f2; // { dg-error "1:invalid use of template-name 'N::A' without an argument list" "" { target c++14_down } }
// { dg-error "deduction|no match" "" { target c++1z } .-1 }
N::INVALID f3; // { dg-error "4:'INVALID' in namespace 'N' does not name a type" } N::INVALID f3; // { dg-error "4:'INVALID' in namespace 'N' does not name a type" }
N::C::INVALID f4; // { dg-error "7:'INVALID' in 'struct N::C' does not name a type" } N::C::INVALID f4; // { dg-error "7:'INVALID' in 'struct N::C' does not name a type" }
N::K f6; // { dg-error "4:'K' in namespace 'N' does not name a type" } N::K f6; // { dg-error "4:'K' in namespace 'N' does not name a type" }
typename N::A f7; typename N::A f7;
// { dg-error "13:invalid use of template-name 'N::A' without an argument list" "13" { target *-*-* } 17 } // { dg-error "13:invalid use of template-name 'N::A' without an argument list" "13" { target *-*-* } .-1 }
struct B struct B
{ {
...@@ -24,7 +25,7 @@ struct B ...@@ -24,7 +25,7 @@ struct B
N::C::INVALID f4; // { dg-error "9:'INVALID' in 'struct N::C' does not name a type" } N::C::INVALID f4; // { dg-error "9:'INVALID' in 'struct N::C' does not name a type" }
N::K f6; // { dg-error "6:'K' in namespace 'N' does not name a type" } N::K f6; // { dg-error "6:'K' in namespace 'N' does not name a type" }
typename N::A f7; typename N::A f7;
// { dg-error "15:invalid use of template-name 'N::A' without an argument list" "15" { target *-*-* } 26 } // { dg-error "15:invalid use of template-name 'N::A' without an argument list" "15" { target *-*-* } .-1 }
}; };
template <int> template <int>
...@@ -36,5 +37,3 @@ struct C ...@@ -36,5 +37,3 @@ struct C
N::K f6; // { dg-error "6:'K' in namespace 'N' does not name a type" } N::K f6; // { dg-error "6:'K' in namespace 'N' does not name a type" }
typename N::A f7; // { dg-error "15:invalid use of template-name 'N::A' without an argument list" } typename N::A f7; // { dg-error "15:invalid use of template-name 'N::A' without an argument list" }
}; };
// { dg-bogus "bogus excess errors in declaration" "bogus excess errors in declaration" { target *-*-* } 17 }
...@@ -9,11 +9,11 @@ namespace H { ...@@ -9,11 +9,11 @@ namespace H {
struct B {}; struct B {};
} }
A a; // { dg-error "template" } A a; // { dg-error "template|no match" }
H::B b; // { dg-error "template" } H::B b; // { dg-error "template|no match" }
int main() { int main() {
A a; // { dg-error "template" } A a; // { dg-error "template|no match" }
H::B b; // { dg-error "template" } H::B b; // { dg-error "template|no match" }
return 0; return 0;
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment