From a56c0ac08242269bbcc4bd1f480eda2378336776 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Thu, 9 Feb 2017 15:55:54 -0500 Subject: [PATCH] PR c++/79316 - default argument in deduction guide PR c++/79350 - explicit deduction guide * parser.c (cp_parser_constructor_declarator_p) (cp_parser_direct_declarator): Parse deduction guides more like constructors. * cp-tree.h (enum special_function_kind): Add sfk_deduction_guide. * tree.c (special_function_p): Return it. * decl.c (check_special_function_return_type): Handle it. (grokdeclarator, grokfndecl): Adjust. (cp_finish_decl): Pass flags to do_auto_deduction. * error.c (dump_decl_name): Use TFF_UNQUALIFIED_NAME. * pt.c (dguide_name_p): Take a const_tree. (do_class_deduction): Handle explicit. (do_auto_deduction): Pass flags through. (build_deduction_guide): Copy explicit flag. From-SVN: r245314 --- gcc/cp/ChangeLog | 18 ++++ gcc/cp/cp-tree.h | 6 +- gcc/cp/decl.c | 49 ++++++---- gcc/cp/error.c | 2 +- gcc/cp/parser.c | 91 +++++++++++-------- gcc/cp/pt.c | 58 ++++++++++-- gcc/cp/tree.c | 2 + .../g++.dg/cpp1z/class-deduction27.C | 5 + .../g++.dg/cpp1z/class-deduction28.C | 24 +++++ gcc/testsuite/g++.dg/cpp1z/class-deduction9.C | 2 +- 10 files changed, 191 insertions(+), 66 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction27.C create mode 100644 gcc/testsuite/g++.dg/cpp1z/class-deduction28.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 6639fbdab9e..caa1df91383 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,21 @@ +2017-02-09 Jason Merrill + + PR c++/79316 - default argument in deduction guide + PR c++/79350 - explicit deduction guide + * parser.c (cp_parser_constructor_declarator_p) + (cp_parser_direct_declarator): Parse deduction guides more like + constructors. + * cp-tree.h (enum special_function_kind): Add sfk_deduction_guide. + * tree.c (special_function_p): Return it. + * decl.c (check_special_function_return_type): Handle it. + (grokdeclarator, grokfndecl): Adjust. + (cp_finish_decl): Pass flags to do_auto_deduction. + * error.c (dump_decl_name): Use TFF_UNQUALIFIED_NAME. + * pt.c (dguide_name_p): Take a const_tree. + (do_class_deduction): Handle explicit. + (do_auto_deduction): Pass flags through. + (build_deduction_guide): Copy explicit flag. + 2017-02-09 Jakub Jelinek PR c++/79429 diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 77bf614a806..a4109261d48 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4733,6 +4733,7 @@ enum special_function_kind { deletes the object after it has been destroyed. */ sfk_conversion, /* A conversion operator. */ + sfk_deduction_guide, /* A class template deduction guide. */ sfk_inheriting_constructor /* An inheriting constructor */ }; @@ -6150,7 +6151,8 @@ extern tree do_auto_deduction (tree, tree, tree); extern tree do_auto_deduction (tree, tree, tree, tsubst_flags_t, auto_deduction_context, - tree = NULL_TREE); + tree = NULL_TREE, + int = LOOKUP_NORMAL); extern tree type_uses_auto (tree); extern tree type_uses_auto_or_concept (tree); extern void append_type_to_template_for_access_check (tree, tree, tree, @@ -6280,7 +6282,7 @@ extern tree extract_fnparm_pack (tree, tree *); extern tree template_parm_to_arg (tree); extern tree dguide_name (tree); extern bool dguide_name_p (tree); -extern bool deduction_guide_p (tree); +extern bool deduction_guide_p (const_tree); /* in repo.c */ extern void init_repo (void); diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index d1d485a4fed..734a6c751d0 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -6821,7 +6821,8 @@ cp_finish_decl (tree decl, tree init, bool init_const_expr_p, if (VAR_P (decl) && DECL_DECOMPOSITION_P (decl)) adc = adc_decomp_type; type = TREE_TYPE (decl) = do_auto_deduction (type, d_init, auto_node, - tf_warning_or_error, adc); + tf_warning_or_error, adc, + NULL_TREE, flags); if (type == error_mark_node) return; if (TREE_CODE (type) == FUNCTION_TYPE) @@ -8730,14 +8731,6 @@ grokfndecl (tree ctype, "namespace scope", decl); return NULL_TREE; } - tree type = TREE_TYPE (DECL_NAME (decl)); - if (CP_DECL_CONTEXT (decl) != CP_TYPE_CONTEXT (type)) - { - error_at (location, "deduction guide %qD must be declared in the " - "same scope as %qT", decl, type); - inform (location_of (type), " declared here"); - return NULL_TREE; - } if (funcdef_flag) error_at (location, "deduction guide %qD must not have a function body", decl); @@ -9758,6 +9751,20 @@ check_special_function_return_type (special_function_kind sfk, type = optype; break; + case sfk_deduction_guide: + if (type) + error ("return type specified for deduction guide"); + else if (type_quals != TYPE_UNQUALIFIED) + error_at (smallest_type_quals_location (type_quals, locations), + "qualifiers are not allowed on declaration of " + "deduction guide"); + type = make_template_placeholder (CLASSTYPE_TI_TEMPLATE (optype)); + for (int i = 0; i < ds_last; ++i) + if (i != ds_explicit && locations[i]) + error_at (locations[i], + "decl-specifier in declaration of deduction guide"); + break; + default: gcc_unreachable (); } @@ -10105,7 +10112,6 @@ grokdeclarator (const cp_declarator *declarator, { gcc_assert (flags == NO_SPECIAL); flags = TYPENAME_FLAG; - ctor_return_type = TREE_TYPE (dname); sfk = sfk_conversion; if (is_typename_at_global_scope (dname)) name = identifier_to_locale (IDENTIFIER_POINTER (dname)); @@ -10285,8 +10291,9 @@ grokdeclarator (const cp_declarator *declarator, #endif typedef_type = type; - - if (sfk != sfk_conversion) + if (sfk == sfk_conversion || sfk == sfk_deduction_guide) + ctor_return_type = TREE_TYPE (dname); + else ctor_return_type = ctype; if (sfk != sfk_none) @@ -10906,12 +10913,13 @@ grokdeclarator (const cp_declarator *declarator, if (!late_return_type) { if (dguide_name_p (unqualified_id)) - error_at (typespec_loc, "deduction guide for " - "%qT must have trailing return type", - TREE_TYPE (tmpl)); + error_at (declarator->id_loc, "deduction guide " + "for %qT must have trailing return " + "type", TREE_TYPE (tmpl)); else - error_at (typespec_loc, "deduced class type %qT " - "in function return type", type); + error_at (declarator->id_loc, "deduced class " + "type %qT in function return type", + type); inform (DECL_SOURCE_LOCATION (tmpl), "%qD declared here", tmpl); } @@ -11039,6 +11047,11 @@ grokdeclarator (const cp_declarator *declarator, if (late_return_type_p) error ("a conversion function cannot have a trailing return type"); } + else if (sfk == sfk_deduction_guide) + { + if (explicitp == 1) + explicitp = 2; + } arg_types = grokparms (declarator->u.function.parameters, &parms); @@ -12207,6 +12220,8 @@ grokdeclarator (const cp_declarator *declarator, if (decl == NULL_TREE) return error_mark_node; + if (explicitp == 2) + DECL_NONCONVERTING_P (decl) = 1; if (staticp == 1) { int invalid_static = 0; diff --git a/gcc/cp/error.c b/gcc/cp/error.c index 92137f7c021..d8c5d5ea412 100644 --- a/gcc/cp/error.c +++ b/gcc/cp/error.c @@ -1015,7 +1015,7 @@ dump_decl_name (cxx_pretty_printer *pp, tree t, int flags) if (dguide_name_p (t)) { dump_decl (pp, CLASSTYPE_TI_TEMPLATE (TREE_TYPE (t)), - TFF_PLAIN_IDENTIFIER); + TFF_UNQUALIFIED_NAME); return; } diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index aadf44d0a60..8812f8ce77b 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -19050,26 +19050,9 @@ cp_parser_init_declarator (cp_parser* parser, token = cp_lexer_peek_token (parser->lexer); - cp_parser_declarator_kind cdk = CP_PARSER_DECLARATOR_NAMED; - if (token->type == CPP_OPEN_PAREN - && decl_specifiers->type - && is_auto (decl_specifiers->type) - && CLASS_PLACEHOLDER_TEMPLATE (decl_specifiers->type)) - { - // C++17 deduction guide. - cdk = CP_PARSER_DECLARATOR_ABSTRACT; - - for (int i = 0; i < ds_last; ++i) - if (i != ds_type_spec - && decl_specifiers->locations[i] - && !cp_parser_simulate_error (parser)) - error_at (decl_specifiers->locations[i], - "decl-specifier in declaration of deduction guide"); - } - /* Parse the declarator. */ declarator - = cp_parser_declarator (parser, cdk, + = cp_parser_declarator (parser, CP_PARSER_DECLARATOR_NAMED, &ctor_dtor_or_conv_p, /*parenthesized_p=*/NULL, member_p, friend_p); @@ -19083,17 +19066,6 @@ cp_parser_init_declarator (cp_parser* parser, if (declarator == cp_error_declarator) return error_mark_node; - if (cdk == CP_PARSER_DECLARATOR_ABSTRACT) - { - gcc_assert (declarator->kind == cdk_function - && !declarator->declarator); - tree t = CLASS_PLACEHOLDER_TEMPLATE (decl_specifiers->type); - declarator->declarator = make_id_declarator (NULL_TREE, dguide_name (t), - sfk_none); - declarator->declarator->id_loc - = decl_specifiers->locations[ds_type_spec]; - } - /* Check that the number of template-parameter-lists is OK. */ if (!cp_parser_check_declarator_template_parameters (parser, declarator, token->location)) @@ -19136,6 +19108,25 @@ cp_parser_init_declarator (cp_parser* parser, if (function_declarator_p (declarator)) { + /* Handle C++17 deduction guides. */ + if (!decl_specifiers->type + && ctor_dtor_or_conv_p <= 0 + && cxx_dialect >= cxx1z) + { + cp_declarator *id = get_id_declarator (declarator); + tree name = id->u.id.unqualified_name; + parser->scope = id->u.id.qualifying_scope; + tree tmpl = cp_parser_lookup_name_simple (parser, name, id->id_loc); + if (tmpl + && (DECL_CLASS_TEMPLATE_P (tmpl) + || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))) + { + id->u.id.unqualified_name = dguide_name (tmpl); + id->u.id.sfk = sfk_deduction_guide; + ctor_dtor_or_conv_p = 1; + } + } + /* Check to see if the token indicates the start of a function-definition. */ if (cp_parser_token_starts_function_definition_p (token)) @@ -19202,8 +19193,8 @@ cp_parser_init_declarator (cp_parser* parser, /* [dcl.dcl] - Only in function declarations for constructors, destructors, and - type conversions can the decl-specifier-seq be omitted. + Only in function declarations for constructors, destructors, type + conversions, and deduction guides can the decl-specifier-seq be omitted. We explicitly postpone this check past the point where we handle function-definitions because we tolerate function-definitions @@ -19453,8 +19444,8 @@ cp_parser_init_declarator (cp_parser* parser, attributes [opt] direct-abstract-declarator If CTOR_DTOR_OR_CONV_P is not NULL, *CTOR_DTOR_OR_CONV_P is used to - detect constructor, destructor or conversion operators. It is set - to -1 if the declarator is a name, and +1 if it is a + detect constructors, destructors, deduction guides, or conversion operators. + It is set to -1 if the declarator is a name, and +1 if it is a function. Otherwise it is set to zero. Usually you just want to test for >0, but internally the negative value is used. @@ -25929,8 +25920,8 @@ cp_parser_global_scope_opt (cp_parser* parser, bool current_scope_valid_p) } /* Returns TRUE if the upcoming token sequence is the start of a - constructor declarator. If FRIEND_P is true, the declarator is - preceded by the `friend' specifier. */ + constructor declarator or C++17 deduction guide. If FRIEND_P is true, the + declarator is preceded by the `friend' specifier. */ static bool cp_parser_constructor_declarator_p (cp_parser *parser, bool friend_p) @@ -25975,8 +25966,10 @@ cp_parser_constructor_declarator_p (cp_parser *parser, bool friend_p) || friend_p); /* Outside of a class-specifier, there must be a - nested-name-specifier. */ - if (!nested_name_specifier && outside_class_specifier_p) + nested-name-specifier. Except in C++17 mode, where we + might be declaring a guiding declaration. */ + if (!nested_name_specifier && outside_class_specifier_p + && cxx_dialect < cxx1z) constructor_p = false; else if (nested_name_specifier == error_mark_node) constructor_p = false; @@ -26007,6 +26000,9 @@ cp_parser_constructor_declarator_p (cp_parser *parser, bool friend_p) }; we must recognize that the nested `S' names a class. */ + if (cxx_dialect >= cxx1z) + cp_parser_parse_tentatively (parser); + tree type_decl; type_decl = cp_parser_class_name (parser, /*typename_keyword_p=*/false, @@ -26015,6 +26011,24 @@ cp_parser_constructor_declarator_p (cp_parser *parser, bool friend_p) /*check_dependency_p=*/false, /*class_head_p=*/false, /*is_declaration=*/false); + + if (cxx_dialect >= cxx1z + && !cp_parser_parse_definitely (parser)) + { + type_decl = NULL_TREE; + tree tmpl = cp_parser_template_name (parser, + /*template_keyword*/false, + /*check_dependency_p*/false, + /*is_declaration*/false, + none_type, + /*is_identifier*/NULL); + if (DECL_CLASS_TEMPLATE_P (tmpl) + || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl)) + /* It's a deduction guide, return true. */; + else + cp_parser_simulate_error (parser); + } + /* If there was no class-name, then this is not a constructor. Otherwise, if we are in a class-specifier and we aren't handling a friend declaration, check that its type matches @@ -26022,6 +26036,7 @@ cp_parser_constructor_declarator_p (cp_parser *parser, bool friend_p) is left alone for error recovery purposes. */ constructor_p = (!cp_parser_error_occurred (parser) && (outside_class_specifier_p + || type_decl == NULL_TREE || type_decl == error_mark_node || same_type_p (current_class_type, TREE_TYPE (type_decl)))); @@ -26056,7 +26071,7 @@ cp_parser_constructor_declarator_p (cp_parser *parser, bool friend_p) in the scope of the class. */ if (current_class_type) type = NULL_TREE; - else + else if (type_decl) { type = TREE_TYPE (type_decl); if (TREE_CODE (type) == TYPENAME_TYPE) diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 8863c281ad7..58d60160e98 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -24786,7 +24786,7 @@ dguide_name_p (tree name) /* True if FN is a deduction guide. */ bool -deduction_guide_p (tree fn) +deduction_guide_p (const_tree fn) { if (DECL_P (fn)) if (tree name = DECL_NAME (fn)) @@ -24999,6 +24999,7 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain) dguide_name (type), fntype); DECL_ARGUMENTS (ded_fn) = fargs; DECL_ARTIFICIAL (ded_fn) = true; + DECL_NONCONVERTING_P (ded_fn) = DECL_NONCONVERTING_P (ctor); tree ded_tmpl = build_template_decl (ded_fn, tparms, /*member*/false); DECL_ARTIFICIAL (ded_tmpl) = true; DECL_TEMPLATE_RESULT (ded_tmpl) = ded_fn; @@ -25015,8 +25016,9 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain) template TMPL based on the initializer INIT, and return the resulting type. */ -tree -do_class_deduction (tree ptype, tree tmpl, tree init, tsubst_flags_t complain) +static tree +do_class_deduction (tree ptype, tree tmpl, tree init, int flags, + tsubst_flags_t complain) { if (!DECL_CLASS_TEMPLATE_P (tmpl)) { @@ -25083,9 +25085,48 @@ do_class_deduction (tree ptype, tree tmpl, tree init, tsubst_flags_t complain) return error_mark_node; } + /* Prune explicit deduction guides in copy-initialization context. */ + tree old_cands = cands; + if (flags & LOOKUP_ONLYCONVERTING) + { + tree t = cands; + for (; t; t = OVL_NEXT (t)) + if (DECL_NONCONVERTING_P (DECL_TEMPLATE_RESULT (OVL_CURRENT (t)))) + break; + if (t) + { + tree pruned = NULL_TREE; + for (t = cands; t; t = OVL_NEXT (t)) + { + tree f = OVL_CURRENT (t); + if (!DECL_NONCONVERTING_P (DECL_TEMPLATE_RESULT (f))) + pruned = build_overload (f, pruned); + } + cands = pruned; + if (cands == NULL_TREE) + { + error ("cannot deduce template arguments for copy-initialization" + " of %qT, as it has no non-explicit deduction guides or " + "user-declared constructors", type); + return error_mark_node; + } + } + } + ++cp_unevaluated_operand; tree t = build_new_function_call (cands, &args, /*koenig*/false, - complain|tf_decltype); + tf_decltype); + + if (t == error_mark_node && (complain & tf_warning_or_error)) + { + error ("class template argument deduction failed:"); + t = build_new_function_call (cands, &args, /*koenig*/false, + complain | tf_decltype); + if (old_cands != cands) + inform (input_location, "explicit deduction guides not considered " + "for copy-initialization"); + } + --cp_unevaluated_operand; release_tree_vector (args); @@ -25106,7 +25147,10 @@ do_auto_deduction (tree type, tree init, tree auto_node) /* Replace occurrences of 'auto' in TYPE with the appropriate type deduced from INIT. AUTO_NODE is the TEMPLATE_TYPE_PARM used for 'auto' in TYPE. The CONTEXT determines the context in which auto deduction is performed - and is used to control error diagnostics. + and is used to control error diagnostics. FLAGS are the LOOKUP_* flags. + OUTER_TARGS are used during template argument deduction + (context == adc_unify) to properly substitute the result, and is ignored + in other contexts. For partial-concept-ids, extra args may be appended to the list of deduced template arguments prior to determining constraint satisfaction. */ @@ -25114,7 +25158,7 @@ do_auto_deduction (tree type, tree init, tree auto_node) tree do_auto_deduction (tree type, tree init, tree auto_node, tsubst_flags_t complain, auto_deduction_context context, - tree outer_targs) + tree outer_targs, int flags) { tree targs; @@ -25129,7 +25173,7 @@ do_auto_deduction (tree type, tree init, tree auto_node, if (tree tmpl = CLASS_PLACEHOLDER_TEMPLATE (auto_node)) /* C++17 class template argument deduction. */ - return do_class_deduction (type, tmpl, init, complain); + return do_class_deduction (type, tmpl, init, flags, complain); /* [dcl.spec.auto]: Obtain P from T by replacing the occurrences of auto with either a new invented type template parameter U or, if the diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index afd442f5801..785dfafae54 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -4374,6 +4374,8 @@ special_function_p (const_tree decl) return sfk_deleting_destructor; if (DECL_CONV_FN_P (decl)) return sfk_conversion; + if (deduction_guide_p (decl)) + return sfk_deduction_guide; return sfk_none; } diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction27.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction27.C new file mode 100644 index 00000000000..ce5c5d73e9f --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction27.C @@ -0,0 +1,5 @@ +// PR c++/79316 +// { dg-options -std=c++1z } + + template struct S { S(T t) {} }; + template S(T, int = 7) -> S; diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction28.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction28.C new file mode 100644 index 00000000000..c91ec0dccaf --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction28.C @@ -0,0 +1,24 @@ +// PR c++/79350 +// { dg-options -std=c++1z } + +template +struct A +{ + explicit A(T); +}; + + +A a (42); +A a2 = 42; // { dg-error "" } + +template +struct B +{ + B(T*); +}; + +template +explicit B(T) -> B; + +B b1 (0); +B b2 = 0; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction9.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction9.C index 5a2b4f6e151..149ef43e5ca 100644 --- a/gcc/testsuite/g++.dg/cpp1z/class-deduction9.C +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction9.C @@ -10,7 +10,7 @@ namespace N { } template -N::A(T) -> N::A; // { dg-error "scope" } +N::A(T) -> N::A; // { dg-error "should have been declared inside .N" } namespace N { template -- 2.30.2