From: Jason Merrill Date: Tue, 28 Feb 2017 23:57:09 +0000 (-0500) Subject: Class template argument deduction refinements X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=853ef4e5632df32894b2366096c1ae1ef4ba03df;p=gcc.git 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 --- diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index e62bdb3420c..b0e589c812d 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,13 @@ +2017-02-28 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. + 2017-02-28 Jakub Jelinek * decl.c (find_decomp_class_base): Use cond ? G_("...") : G_("...") diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 560804ab2d5..babab00158d 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -9670,18 +9670,6 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, 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, F1 is a non-template function and F2 is a template function specialization. */ @@ -9719,6 +9707,18 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, 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 conversion sequences are equivalent. (proposed in http://lists.isocpp.org/core/2016/10/1142.php) */ diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 54cbbb70c01..3e7316f3e0b 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -5238,13 +5238,15 @@ start_decl_1 (tree decl, bool initialized) else if (aggregate_definition_p && !complete_p) { if (type_uses_auto (type)) - gcc_unreachable (); + gcc_assert (CLASS_PLACEHOLDER_TEMPLATE (type)); else - error ("aggregate %q#D has incomplete type and cannot be defined", - decl); - /* Change the type so that assemble_variable will give - DECL an rtl we can live with: (mem (const_int 0)). */ - type = TREE_TYPE (decl) = error_mark_node; + { + error ("aggregate %q#D has incomplete type and cannot be defined", + decl); + /* Change the type so that assemble_variable will give + DECL an rtl we can live with: (mem (const_int 0)). */ + type = TREE_TYPE (decl) = error_mark_node; + } } /* Create a new scope to hold this declaration if necessary. @@ -6816,14 +6818,17 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, return; } - gcc_unreachable (); + gcc_assert (CLASS_PLACEHOLDER_TEMPLATE (auto_node)); } d_init = init; - if (TREE_CODE (d_init) == TREE_LIST - && !CLASS_PLACEHOLDER_TEMPLATE (auto_node)) - d_init = build_x_compound_expr_from_list (d_init, ELK_INIT, - tf_warning_or_error); - d_init = resolve_nondeduced_context (d_init, tf_warning_or_error); + if (d_init) + { + if (TREE_CODE (d_init) == TREE_LIST + && !CLASS_PLACEHOLDER_TEMPLATE (auto_node)) + d_init = build_x_compound_expr_from_list (d_init, ELK_INIT, + tf_warning_or_error); + d_init = resolve_nondeduced_context (d_init, tf_warning_or_error); + } enum auto_deduction_context adc = adc_variable_type; if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl)) adc = adc_decomp_type; @@ -12323,19 +12328,12 @@ grokdeclarator (const cp_declarator *declarator, if (VAR_P (decl) && !initialized) if (tree auto_node = type_uses_auto (type)) - { - 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 + if (!CLASS_PLACEHOLDER_TEMPLATE (auto_node)) + { + location_t loc = declspecs->locations[ds_type_spec]; error_at (loc, "declaration of %q#D has no initializer", decl); - TREE_TYPE (decl) = error_mark_node; - } + TREE_TYPE (decl) = error_mark_node; + } if (storage_class == sc_extern && initialized && !funcdef_flag) { diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index d5428ed9feb..ec9d53a83ee 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -24941,103 +24941,137 @@ rewrite_template_parm (tree olddecl, unsigned index, unsigned level, } /* 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 build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain) { - if (outer_args) - ctor = tsubst (ctor, outer_args, complain, ctor); - tree type = DECL_CONTEXT (ctor); - tree fn_tmpl; - if (TREE_CODE (ctor) == TEMPLATE_DECL) + tree type, tparms, targs, fparms, fargs, ci; + bool memtmpl = false; + bool explicit_p; + location_t loc; + + if (TYPE_P (ctor)) { - fn_tmpl = ctor; - ctor = DECL_TEMPLATE_RESULT (fn_tmpl); + 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 - fn_tmpl = DECL_TI_TEMPLATE (ctor); - - tree tparms = DECL_TEMPLATE_PARMS (fn_tmpl); - /* If type is a member class template, DECL_TI_ARGS (ctor) will have fully - specialized args for the enclosing class. Strip those off, as the - deduction guide won't have those template parameters. */ - tree targs = get_innermost_template_args (DECL_TI_ARGS (ctor), - TMPL_PARMS_DEPTH (tparms)); - /* Discard the 'this' parameter. */ - tree fparms = FUNCTION_ARG_CHAIN (ctor); - tree fargs = TREE_CHAIN (DECL_ARGUMENTS (ctor)); - tree ci = get_constraints (ctor); - - if (PRIMARY_TEMPLATE_P (fn_tmpl)) - { - /* 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; - tree save_parms = current_template_parms; - - /* For a member template we should have two levels of parms/args, one for - the class and one for the constructor. We stripped specialized args - for further enclosing classes above. */ - const int depth = 2; - gcc_assert (TMPL_ARGS_DEPTH (targs) == depth); - - /* Template args for translating references to the two-level template - parameters into references to the one-level template parameters we are - creating. */ - tree tsubst_args = copy_node (targs); - TMPL_ARGS_LEVEL (tsubst_args, depth) - = copy_node (TMPL_ARGS_LEVEL (tsubst_args, depth)); - - /* Template parms for the constructor template. */ - tree ftparms = TREE_VALUE (tparms); - unsigned flen = TREE_VEC_LENGTH (ftparms); - /* Template parms for the class template. */ - tparms = TREE_CHAIN (tparms); - tree ctparms = TREE_VALUE (tparms); - unsigned clen = TREE_VEC_LENGTH (ctparms); - /* Template parms for the deduction guide start as a copy of the template - parms for the class. We set current_template_parms for - lookup_template_class_1. */ - current_template_parms = tparms = copy_node (tparms); - tree new_vec = TREE_VALUE (tparms) = make_tree_vec (flen + clen); - for (unsigned i = 0; i < clen; ++i) - TREE_VEC_ELT (new_vec, i) = TREE_VEC_ELT (ctparms, i); - - /* Now we need to rewrite the constructor parms to append them to the - class parms. */ - for (unsigned i = 0; i < flen; ++i) + { + if (outer_args) + ctor = tsubst (ctor, outer_args, complain, ctor); + type = DECL_CONTEXT (ctor); + tree fn_tmpl; + if (TREE_CODE (ctor) == TEMPLATE_DECL) { - unsigned index = i + clen; - unsigned level = 1; - tree oldelt = TREE_VEC_ELT (ftparms, i); - tree olddecl = TREE_VALUE (oldelt); - tree newdecl = rewrite_template_parm (olddecl, index, level, - tsubst_args, complain); - tree newdef = tsubst_template_arg (TREE_PURPOSE (oldelt), - tsubst_args, complain, ctor); - tree list = build_tree_list (newdef, newdecl); - TEMPLATE_PARM_CONSTRAINTS (list) - = tsubst_constraint_info (TEMPLATE_PARM_CONSTRAINTS (oldelt), - tsubst_args, complain, ctor); - TREE_VEC_ELT (new_vec, index) = list; - TMPL_ARG (tsubst_args, depth, i) = template_parm_to_arg (list); + fn_tmpl = ctor; + ctor = DECL_TEMPLATE_RESULT (fn_tmpl); } + else + fn_tmpl = DECL_TI_TEMPLATE (ctor); + + tparms = DECL_TEMPLATE_PARMS (fn_tmpl); + /* If type is a member class template, DECL_TI_ARGS (ctor) will have + fully specialized args for the enclosing class. Strip those off, as + the deduction guide won't have those template parameters. */ + targs = get_innermost_template_args (DECL_TI_ARGS (ctor), + TMPL_PARMS_DEPTH (tparms)); + /* Discard the 'this' parameter. */ + fparms = FUNCTION_ARG_CHAIN (ctor); + fargs = TREE_CHAIN (DECL_ARGUMENTS (ctor)); + ci = get_constraints (ctor); + loc = DECL_SOURCE_LOCATION (ctor); + explicit_p = DECL_NONCONVERTING_P (ctor); + + if (PRIMARY_TEMPLATE_P (fn_tmpl)) + { + memtmpl = true; + + /* 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; + tree save_parms = current_template_parms; + + /* For a member template we should have two levels of parms/args, one + for the class and one for the constructor. We stripped + specialized args for further enclosing classes above. */ + const int depth = 2; + gcc_assert (TMPL_ARGS_DEPTH (targs) == depth); + + /* Template args for translating references to the two-level template + parameters into references to the one-level template parameters we + are creating. */ + tree tsubst_args = copy_node (targs); + TMPL_ARGS_LEVEL (tsubst_args, depth) + = copy_node (TMPL_ARGS_LEVEL (tsubst_args, depth)); + + /* Template parms for the constructor template. */ + tree ftparms = TREE_VALUE (tparms); + unsigned flen = TREE_VEC_LENGTH (ftparms); + /* Template parms for the class template. */ + tparms = TREE_CHAIN (tparms); + tree ctparms = TREE_VALUE (tparms); + unsigned clen = TREE_VEC_LENGTH (ctparms); + /* Template parms for the deduction guide start as a copy of the + template parms for the class. We set current_template_parms for + lookup_template_class_1. */ + current_template_parms = tparms = copy_node (tparms); + tree new_vec = TREE_VALUE (tparms) = make_tree_vec (flen + clen); + for (unsigned i = 0; i < clen; ++i) + TREE_VEC_ELT (new_vec, i) = TREE_VEC_ELT (ctparms, i); + + /* Now we need to rewrite the constructor parms to append them to the + class parms. */ + for (unsigned i = 0; i < flen; ++i) + { + unsigned index = i + clen; + unsigned level = 1; + tree oldelt = TREE_VEC_ELT (ftparms, i); + tree olddecl = TREE_VALUE (oldelt); + tree newdecl = rewrite_template_parm (olddecl, index, level, + tsubst_args, complain); + tree newdef = tsubst_template_arg (TREE_PURPOSE (oldelt), + tsubst_args, complain, ctor); + tree list = build_tree_list (newdef, newdecl); + TEMPLATE_PARM_CONSTRAINTS (list) + = tsubst_constraint_info (TEMPLATE_PARM_CONSTRAINTS (oldelt), + tsubst_args, complain, ctor); + TREE_VEC_ELT (new_vec, index) = list; + TMPL_ARG (tsubst_args, depth, i) = template_parm_to_arg (list); + } - /* Now we have a final set of template parms to substitute into the - function signature. */ - targs = template_parms_to_args (tparms); - fparms = tsubst_arg_types (fparms, tsubst_args, NULL_TREE, - complain, ctor); - fargs = tsubst (fargs, tsubst_args, complain, ctor); - if (ci) - ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor); + /* Now we have a final set of template parms to substitute into the + function signature. */ + targs = template_parms_to_args (tparms); + fparms = tsubst_arg_types (fparms, tsubst_args, NULL_TREE, + complain, ctor); + fargs = tsubst (fargs, tsubst_args, complain, ctor); + if (ci) + ci = tsubst_constraint_info (ci, tsubst_args, complain, ctor); - current_template_parms = save_parms; - --processing_template_decl; + current_template_parms = save_parms; + --processing_template_decl; + } } - else + + if (!memtmpl) { /* Copy the parms so we can set DECL_PRIMARY_TEMPLATE. */ tparms = copy_node (tparms); @@ -25046,12 +25080,12 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain) } 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, dguide_name (type), fntype); DECL_ARGUMENTS (ded_fn) = fargs; 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); DECL_ARTIFICIAL (ded_tmpl) = true; DECL_TEMPLATE_RESULT (ded_tmpl) = ded_fn; @@ -25085,27 +25119,16 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, tree type = TREE_TYPE (tmpl); vec *args; - if (TREE_CODE (init) == TREE_LIST) + if (init == NULL_TREE + || TREE_CODE (init) == TREE_LIST) 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); else 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 cands = lookup_qualified_name (CP_DECL_CONTEXT (tmpl), dname, /*type*/false, /*complain*/false, @@ -25121,6 +25144,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, type = TREE_TYPE (most_general_template (tmpl)); } + bool saw_default = false; + bool saw_copy = false; if (CLASSTYPE_METHOD_VEC (type)) // FIXME cache artificial deduction guides 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, tree fn = OVL_CURRENT (fns); tree guide = build_deduction_guide (fn, outer_args, complain); cands = ovl_cons (guide, cands); + + tree parms = FUNCTION_FIRST_USER_PARMTYPE (fn); + if (sufficient_parms_p (parms)) + saw_default = true; + if (parms && sufficient_parms_p (TREE_CHAIN (parms))) + { + 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 (cands == NULL_TREE) + if (!saw_default && args->length() == 0) { - if (args->length() == 0) - { - /* Try tmpl<>. */ - tree t = lookup_template_class (tmpl, NULL_TREE, NULL_TREE, - NULL_TREE, false, tf_none); - if (t != error_mark_node) - return t; - } - error ("cannot deduce template arguments for %qT, as it has " - "no deduction guides or user-declared constructors", type); - return error_mark_node; + tree guide = build_deduction_guide (type, outer_args, complain); + cands = ovl_cons (guide, cands); + } + if (!saw_copy && args->length() == 1) + { + 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. */ @@ -25225,7 +25259,7 @@ do_auto_deduction (tree type, tree init, tree auto_node, if (init == error_mark_node) return error_mark_node; - if (type_dependent_expression_p (init) + if (init && type_dependent_expression_p (init) && context != adc_unify) /* Defining a subset of type-dependent expressions that we can deduce from ahead of time isn't worth the trouble. */ diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction17.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction17.C new file mode 100644 index 00000000000..7f2be00af23 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction17.C @@ -0,0 +1,11 @@ +// { dg-options -std=c++1z } + +#include +template +struct A +{ + A (std::initializer_list); +}; + +A a{1,2}; + diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction25.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction25.C index 07ab5f5738c..0e496e62d85 100644 --- a/gcc/testsuite/g++.dg/cpp1z/class-deduction25.C +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction25.C @@ -15,10 +15,10 @@ template struct A { template::value> A(T&&, int*) -> A; //#3 A a{1,0}; // uses #1 to deduce A and initializes with #1 -A b{a,0}; // uses #3 (not #2) to deduce A&> and initializes with #1 +A b{a,0}; // uses #2 to deduce A and initializes with #2 template struct same; template struct same {}; same> s1; -same&>> s2; +same> s2; diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction30.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction30.C index e182803ae79..f50e87819ea 100644 --- a/gcc/testsuite/g++.dg/cpp1z/class-deduction30.C +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction30.C @@ -3,4 +3,4 @@ template struct A { }; A a{}; - +A a2; diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction31.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction31.C new file mode 100644 index 00000000000..4423157490a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction31.C @@ -0,0 +1,22 @@ +// { dg-options -std=c++1z } + +template struct A { + A(T); // #1 + A(const A&); // #2 +}; + +template A(T) -> A; // #3 + +A a (42); // uses #3 to deduce A and initializes with #1 +A b = a; // uses #2 (not #3) to deduce A and initializes with #2; #2 is more specialized + +template A(A) -> A>; // #4 + +A b2 = a; // uses #4 to deduce A> and initializes with #1; #4 is as specialized as #2 + +template struct same; +template struct same {}; + +same> s1; +same> s2; +same>> s3; diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction32.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction32.C new file mode 100644 index 00000000000..4c3824fdee8 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction32.C @@ -0,0 +1,5 @@ +// { dg-options -std=c++1z } + +#include + +std::initializer_list l { 1, 2, 3 }; diff --git a/gcc/testsuite/g++.dg/parse/error15.C b/gcc/testsuite/g++.dg/parse/error15.C index be002412efb..c32b5d421ff 100644 --- a/gcc/testsuite/g++.dg/parse/error15.C +++ b/gcc/testsuite/g++.dg/parse/error15.C @@ -10,12 +10,13 @@ namespace N 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::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" } 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 { @@ -24,7 +25,7 @@ struct B 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" } 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 @@ -36,5 +37,3 @@ struct C 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" } }; - -// { dg-bogus "bogus excess errors in declaration" "bogus excess errors in declaration" { target *-*-* } 17 } diff --git a/gcc/testsuite/g++.dg/template/error52.C b/gcc/testsuite/g++.dg/template/error52.C index 3350c8e6a78..03068bbf440 100644 --- a/gcc/testsuite/g++.dg/template/error52.C +++ b/gcc/testsuite/g++.dg/template/error52.C @@ -9,11 +9,11 @@ namespace H { struct B {}; } -A a; // { dg-error "template" } -H::B b; // { dg-error "template" } +A a; // { dg-error "template|no match" } +H::B b; // { dg-error "template|no match" } int main() { - A a; // { dg-error "template" } - H::B b; // { dg-error "template" } + A a; // { dg-error "template|no match" } + H::B b; // { dg-error "template|no match" } return 0; }