From: Jason Merrill Date: Wed, 27 Nov 2019 22:05:53 +0000 (-0500) Subject: Implement P1814R0, CTAD for alias templates. X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=1a291106384cabc73da0bc0f457b1cd3a4015970;p=gcc.git Implement P1814R0, CTAD for alias templates. This patch implements C++20 class template argument deduction for alias templates, which works by a moderately arcane transformation of the deduction guides for the underlying class template. When implementing it, it seemed that I could simplify the rules in the draft a bit and get essentially the same effect; I'll be emailing the committee to that effect soon. gcc/cp/ * pt.c (rewrite_tparm_list): Factor out of build_deduction_guide. (maybe_aggr_guide): Check for copy-init here. (alias_ctad_tweaks, deduction_guides_for): New. (ctor_deduction_guides_for): Factor out of do_class_deduction. (ctad_template_p): New. * parser.c (cp_parser_simple_type_specifier): Use it. * constraint.cc (append_constraint): New. gcc/c-family/ * c-cppbuiltin.c (c_cpp_builtins): Update __cpp_deduction_guides. From-SVN: r278786 --- diff --git a/gcc/c-family/ChangeLog b/gcc/c-family/ChangeLog index 89292175846..a26b1f28ead 100644 --- a/gcc/c-family/ChangeLog +++ b/gcc/c-family/ChangeLog @@ -1,3 +1,7 @@ +2019-11-27 Jason Merrill + + * c-cppbuiltin.c (c_cpp_builtins): Update __cpp_deduction_guides. + 2019-11-26 Jakub Jelinek PR c++/61414 diff --git a/gcc/c-family/c-cppbuiltin.c b/gcc/c-family/c-cppbuiltin.c index 50066e4dd8b..6491545bc3b 100644 --- a/gcc/c-family/c-cppbuiltin.c +++ b/gcc/c-family/c-cppbuiltin.c @@ -980,7 +980,8 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_capture_star_this=201603L"); cpp_define (pfile, "__cpp_inline_variables=201606L"); cpp_define (pfile, "__cpp_aggregate_bases=201603L"); - cpp_define (pfile, "__cpp_deduction_guides=201703L"); + if (cxx_dialect <= cxx17) + cpp_define (pfile, "__cpp_deduction_guides=201703L"); cpp_define (pfile, "__cpp_noexcept_function_type=201510L"); /* Old macro, superseded by __cpp_nontype_template_parameter_auto. */ @@ -1000,6 +1001,7 @@ c_cpp_builtins (cpp_reader *pfile) cpp_define (pfile, "__cpp_conditional_explicit=201806L"); cpp_define (pfile, "__cpp_consteval=201811L"); cpp_define (pfile, "__cpp_constinit=201907L"); + cpp_define (pfile, "__cpp_deduction_guides=201907L"); cpp_define (pfile, "__cpp_nontype_template_parameter_class=201806L"); cpp_define (pfile, "__cpp_impl_destroying_delete=201806L"); cpp_define (pfile, "__cpp_constexpr_dynamic_alloc=201907L"); diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 9dbc61c2151..ec37f5ec4f2 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,14 @@ +2019-11-16 Jason Merrill + + Implement P1814R0, CTAD for alias templates. + * pt.c (rewrite_tparm_list): Factor out of build_deduction_guide. + (maybe_aggr_guide): Check for copy-init here. + (alias_ctad_tweaks, deduction_guides_for): New. + (ctor_deduction_guides_for): Factor out of do_class_deduction. + (ctad_template_p): New. + * parser.c (cp_parser_simple_type_specifier): Use it. + * constraint.cc (append_constraint): New. + 2019-11-16 Jason Merrill * cxx-pretty-print.c (pp_cxx_unqualified_id): Handle alias diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 8bfe3368816..acc7e1322d0 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -4398,7 +4398,7 @@ build_converted_constant_bool_expr (tree expr, tsubst_flags_t complain) /* Do any initial processing on the arguments to a function call. */ -static vec * +vec * resolve_args (vec *args, tsubst_flags_t complain) { unsigned int ix; diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc index 0d1c27a6d16..533277a758f 100644 --- a/gcc/cp/constraint.cc +++ b/gcc/cp/constraint.cc @@ -1116,6 +1116,25 @@ build_constraints (tree tr, tree dr) return (tree)ci; } +/* Add constraint RHS to the end of CONSTRAINT_INFO ci. */ + +tree +append_constraint (tree ci, tree rhs) +{ + tree tr = ci ? CI_TEMPLATE_REQS (ci) : NULL_TREE; + tree dr = ci ? CI_DECLARATOR_REQS (ci) : NULL_TREE; + dr = combine_constraint_expressions (dr, rhs); + if (ci) + { + CI_DECLARATOR_REQS (ci) = dr; + tree ac = combine_constraint_expressions (tr, dr); + CI_ASSOCIATED_CONSTRAINTS (ci) = ac; + } + else + ci = build_constraints (tr, dr); + return ci; +} + /* A mapping from declarations to constraint information. */ static GTY ((cache)) decl_tree_cache_map *decl_constraints; diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index fd3be60d407..7e810b8ee7b 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -6320,6 +6320,7 @@ extern tree build_converted_constant_expr (tree, tree, tsubst_flags_t); extern tree build_converted_constant_bool_expr (tree, tsubst_flags_t); extern tree perform_direct_initialization_if_possible (tree, tree, bool, tsubst_flags_t); +extern vec *resolve_args (vec*, tsubst_flags_t); extern tree in_charge_arg_for_name (tree); extern tree build_cxx_call (tree, int, tree *, tsubst_flags_t, @@ -6820,6 +6821,7 @@ extern tree make_constrained_auto (tree, tree); extern tree make_constrained_decltype_auto (tree, tree); extern tree make_template_placeholder (tree); extern bool template_placeholder_p (tree); +extern bool ctad_template_p (tree); extern tree do_auto_deduction (tree, tree, tree, tsubst_flags_t = tf_warning_or_error, diff --git a/gcc/cp/parser.c b/gcc/cp/parser.c index c08b7b32a32..ed2441644f1 100644 --- a/gcc/cp/parser.c +++ b/gcc/cp/parser.c @@ -18082,8 +18082,7 @@ cp_parser_simple_type_specifier (cp_parser* parser, /*ambiguous_decls=*/NULL, token->location); if (tmpl && tmpl != error_mark_node - && (DECL_CLASS_TEMPLATE_P (tmpl) - || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl))) + && ctad_template_p (tmpl)) type = make_template_placeholder (tmpl); else if (flag_concepts && tmpl && concept_definition_p (tmpl)) type = cp_parser_placeholder_type_specifier (parser, loc, diff --git a/gcc/cp/pt.c b/gcc/cp/pt.c index 6e712bdb4e1..5088dc125ce 100644 --- a/gcc/cp/pt.c +++ b/gcc/cp/pt.c @@ -27784,6 +27784,29 @@ rewrite_template_parm (tree olddecl, unsigned index, unsigned level, return newdecl; } +/* As rewrite_template_parm, but for the whole TREE_LIST representing a + template parameter. */ + +static tree +rewrite_tparm_list (tree oldelt, unsigned index, unsigned level, + tree targs, unsigned targs_index, tsubst_flags_t complain) +{ + tree olddecl = TREE_VALUE (oldelt); + tree newdecl = rewrite_template_parm (olddecl, index, level, + targs, complain); + if (newdecl == error_mark_node) + return error_mark_node; + tree newdef = tsubst_template_arg (TREE_PURPOSE (oldelt), + targs, complain, NULL_TREE); + tree list = build_tree_list (newdef, newdecl); + TEMPLATE_PARM_CONSTRAINTS (list) + = tsubst_constraint_info (TEMPLATE_PARM_CONSTRAINTS (oldelt), + targs, complain, NULL_TREE); + int depth = TMPL_ARGS_DEPTH (targs); + TMPL_ARG (targs, depth, targs_index) = template_parm_to_arg (list); + return list; +} + /* Returns a C++17 class deduction guide template based on the constructor CTOR. As a special case, CTOR can be a RECORD_TYPE for an implicit default guide, REFERENCE_TYPE for an implicit copy/move guide, or TREE_LIST for an @@ -27897,19 +27920,12 @@ build_deduction_guide (tree type, tree ctor, tree outer_args, tsubst_flags_t com 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); - if (newdecl == error_mark_node) + tree newelt + = rewrite_tparm_list (oldelt, index, level, + tsubst_args, i, complain); + if (newelt == error_mark_node) ok = false; - 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); + TREE_VEC_ELT (new_vec, index) = newelt; } /* Now we have a final set of template parms to substitute into the @@ -27984,20 +28000,48 @@ collect_ctor_idx_types (tree ctor, tree list) return list; } +/* Return whether ETYPE is, or is derived from, a specialization of TMPL. */ + +static bool +is_spec_or_derived (tree etype, tree tmpl) +{ + if (!etype || !CLASS_TYPE_P (etype)) + return false; + + tree type = TREE_TYPE (tmpl); + tree tparms = (INNERMOST_TEMPLATE_PARMS + (DECL_TEMPLATE_PARMS (tmpl))); + tree targs = make_tree_vec (TREE_VEC_LENGTH (tparms)); + int err = unify (tparms, targs, type, etype, + UNIFY_ALLOW_DERIVED, /*explain*/false); + ggc_free (targs); + return !err; +} + /* Return a C++20 aggregate deduction candidate for TYPE initialized from INIT. */ static tree -maybe_aggr_guide (tree type, tree init) +maybe_aggr_guide (tree tmpl, tree init, vec *args) { if (cxx_dialect < cxx2a) return NULL_TREE; if (init == NULL_TREE) return NULL_TREE; + + tree type = TREE_TYPE (tmpl); if (!CP_AGGREGATE_TYPE_P (type)) return NULL_TREE; + /* No aggregate candidate for copy-initialization. */ + if (args->length() == 1) + { + tree val = (*args)[0]; + if (is_spec_or_derived (tmpl, TREE_TYPE (val))) + return NULL_TREE; + } + /* If we encounter a problem, we just won't add the candidate. */ tsubst_flags_t complain = tf_none; @@ -28039,22 +28083,292 @@ maybe_aggr_guide (tree type, tree init) return NULL_TREE; } -/* Return whether ETYPE is, or is derived from, a specialization of TMPL. */ +/* UGUIDES are the deduction guides for the underlying template of alias + template TMPL; adjust them to be deduction guides for TMPL. */ -static bool -is_spec_or_derived (tree etype, tree tmpl) -{ - if (!etype || !CLASS_TYPE_P (etype)) - return false; +static tree +alias_ctad_tweaks (tree tmpl, tree uguides) +{ + /* [over.match.class.deduct]: When resolving a placeholder for a deduced + class type (9.2.8.2) where the template-name names an alias template A, + the defining-type-id of A must be of the form + + typename(opt) nested-name-specifier(opt) template(opt) simple-template-id + + as specified in 9.2.8.2. The guides of A are the set of functions or + function templates formed as follows. For each function or function + template f in the guides of the template named by the simple-template-id + of the defining-type-id, the template arguments of the return type of f + are deduced from the defining-type-id of A according to the process in + 13.10.2.5 with the exception that deduction does not fail if not all + template arguments are deduced. Let g denote the result of substituting + these deductions into f. If substitution succeeds, form a function or + function template f' with the following properties and add it to the set + of guides of A: + + * The function type of f' is the function type of g. + + * If f is a function template, f' is a function template whose template + parameter list consists of all the template parameters of A (including + their default template arguments) that appear in the above deductions or + (recursively) in their default template arguments, followed by the + template parameters of f that were not deduced (including their default + template arguments), otherwise f' is not a function template. + + * The associated constraints (13.5.2) are the conjunction of the + associated constraints of g and a constraint that is satisfied if and only + if the arguments of A are deducible (see below) from the return type. + + * If f is a copy deduction candidate (12.4.1.8), then f' is considered to + be so as well. + + * If f was generated from a deduction-guide (12.4.1.8), then f' is + considered to be so as well. + + * The explicit-specifier of f' is the explicit-specifier of g (if + any). */ + + /* This implementation differs from the above in two significant ways: + + 1) We include all template parameters of A, not just some. + 2) The added constraint is same_type instead of deducible. + + I believe that while it's probably possible to construct a testcase that + behaves differently with this simplification, it should have the same + effect for real uses. Including all template parameters means that we + deduce all parameters of A when resolving the call, so when we're in the + constraint we don't need to deduce them again, we can just check whether + the deduction produced the desired result. */ + + tsubst_flags_t complain = tf_warning_or_error; + tree atype = TREE_TYPE (tmpl); + tree aguides = NULL_TREE; + tree atparms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (tmpl)); + unsigned natparms = TREE_VEC_LENGTH (atparms); + tree utype = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl)); + for (ovl_iterator iter (uguides); iter; ++iter) + { + tree f = *iter; + tree in_decl = f; + location_t loc = DECL_SOURCE_LOCATION (f); + tree ret = TREE_TYPE (TREE_TYPE (f)); + tree fprime = f; + if (TREE_CODE (f) == TEMPLATE_DECL) + { + processing_template_decl_sentinel ptds (/*reset*/false); + ++processing_template_decl; + + /* Deduce template arguments for f from the type-id of A. */ + tree ftparms = INNERMOST_TEMPLATE_PARMS (DECL_TEMPLATE_PARMS (f)); + unsigned len = TREE_VEC_LENGTH (ftparms); + tree targs = make_tree_vec (len); + int err = unify (ftparms, targs, ret, utype, UNIFY_ALLOW_NONE, false); + gcc_assert (!err); + + /* The number of parms for f' is the number of parms for A plus + non-deduced parms of f. */ + unsigned ndlen = 0; + unsigned j; + for (unsigned i = 0; i < len; ++i) + if (TREE_VEC_ELT (targs, i) == NULL_TREE) + ++ndlen; + tree gtparms = make_tree_vec (natparms + ndlen); + + /* First copy over the parms of A. */ + for (j = 0; j < natparms; ++j) + TREE_VEC_ELT (gtparms, j) = TREE_VEC_ELT (atparms, j); + /* Now rewrite the non-deduced parms of f. */ + for (unsigned i = 0; ndlen && i < len; ++i) + if (TREE_VEC_ELT (targs, i) == NULL_TREE) + { + --ndlen; + unsigned index = j++; + unsigned level = 1; + tree oldlist = TREE_VEC_ELT (ftparms, i); + tree list = rewrite_tparm_list (oldlist, index, level, + targs, i, complain); + TREE_VEC_ELT (gtparms, index) = list; + } + gtparms = build_tree_list (size_one_node, gtparms); + + /* Substitute the deduced arguments plus the rewritten template + parameters into f to get g. This covers the type, copyness, + guideness, and explicit-specifier. */ + tree g = tsubst_decl (DECL_TEMPLATE_RESULT (f), targs, complain); + if (g == error_mark_node) + return error_mark_node; + DECL_USE_TEMPLATE (g) = 0; + fprime = build_template_decl (g, gtparms, false); + DECL_TEMPLATE_RESULT (fprime) = g; + TREE_TYPE (fprime) = TREE_TYPE (g); + tree gtargs = template_parms_to_args (gtparms); + DECL_TEMPLATE_INFO (g) = build_template_info (fprime, gtargs); + DECL_PRIMARY_TEMPLATE (fprime) = fprime; + + /* Substitute the associated constraints. */ + tree ci = get_constraints (f); + if (ci) + ci = tsubst_constraint_info (ci, targs, complain, in_decl); + if (ci == error_mark_node) + return error_mark_node; + + /* Add a constraint that the return type matches the instantiation of + A with the same template arguments. */ + ret = TREE_TYPE (TREE_TYPE (fprime)); + if (!same_type_p (atype, ret) + /* FIXME this should mean they don't compare as equivalent. */ + || dependent_alias_template_spec_p (atype, nt_opaque)) + { + tree same = finish_trait_expr (loc, CPTK_IS_SAME_AS, atype, ret); + ci = append_constraint (ci, same); + } + + if (ci) + set_constraints (fprime, ci); + } + else + { + /* For a non-template deduction guide, if the arguments of A aren't + deducible from the return type, don't add the candidate. */ + tree targs = make_tree_vec (natparms); + int err = unify (atparms, targs, utype, ret, UNIFY_ALLOW_NONE, false); + for (unsigned i = 0; !err && i < natparms; ++i) + if (TREE_VEC_ELT (targs, i) == NULL_TREE) + err = true; + if (err) + continue; + } + aguides = lookup_add (fprime, aguides); + } + + return aguides; +} + +/* Return artificial deduction guides built from the constructors of class + template TMPL. */ + +static tree +ctor_deduction_guides_for (tree tmpl, tsubst_flags_t complain) +{ tree type = TREE_TYPE (tmpl); - tree tparms = (INNERMOST_TEMPLATE_PARMS - (DECL_TEMPLATE_PARMS (tmpl))); - tree targs = make_tree_vec (TREE_VEC_LENGTH (tparms)); - int err = unify (tparms, targs, type, etype, - UNIFY_ALLOW_DERIVED, /*explain*/false); - ggc_free (targs); - return !err; + tree outer_args = NULL_TREE; + if (DECL_CLASS_SCOPE_P (tmpl) + && CLASSTYPE_TEMPLATE_INSTANTIATION (DECL_CONTEXT (tmpl))) + { + outer_args = CLASSTYPE_TI_ARGS (DECL_CONTEXT (tmpl)); + type = TREE_TYPE (most_general_template (tmpl)); + } + + tree cands = NULL_TREE; + + for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (type)); iter; ++iter) + { + /* Skip inherited constructors. */ + if (iter.using_p ()) + continue; + + tree guide = build_deduction_guide (type, *iter, outer_args, complain); + cands = lookup_add (guide, cands); + } + + /* Add implicit default constructor deduction guide. */ + if (!TYPE_HAS_USER_CONSTRUCTOR (type)) + { + tree guide = build_deduction_guide (type, type, outer_args, + complain); + cands = lookup_add (guide, cands); + } + + /* Add copy guide. */ + { + tree gtype = build_reference_type (type); + tree guide = build_deduction_guide (type, gtype, outer_args, + complain); + cands = lookup_add (guide, cands); + } + + return cands; +} + +static GTY((deletable)) hash_map *dguide_cache; + +/* Return the non-aggregate deduction guides for deducible template TMPL. The + aggregate candidate is added separately because it depends on the + initializer. */ + +static tree +deduction_guides_for (tree tmpl, tsubst_flags_t complain) +{ + tree guides = NULL_TREE; + if (DECL_ALIAS_TEMPLATE_P (tmpl)) + { + tree under = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl)); + tree tinfo = get_template_info (under); + guides = deduction_guides_for (TI_TEMPLATE (tinfo), complain); + } + else + { + guides = lookup_qualified_name (CP_DECL_CONTEXT (tmpl), + dguide_name (tmpl), + /*type*/false, /*complain*/false, + /*hidden*/false); + if (guides == error_mark_node) + guides = NULL_TREE; + } + + /* Cache the deduction guides for a template. We also remember the result of + lookup, and rebuild everything if it changes; should be very rare. */ + tree_pair_p cache = NULL; + if (tree_pair_p &r + = hash_map_safe_get_or_insert (dguide_cache, tmpl)) + { + cache = r; + if (cache->purpose == guides) + return cache->value; + } + else + { + r = cache = ggc_cleared_alloc (); + cache->purpose = guides; + } + + tree cands = NULL_TREE; + if (DECL_ALIAS_TEMPLATE_P (tmpl)) + cands = alias_ctad_tweaks (tmpl, guides); + else + { + cands = ctor_deduction_guides_for (tmpl, complain); + for (ovl_iterator it (guides); it; ++it) + cands = lookup_add (*it, cands); + } + + cache->value = cands; + return cands; +} + +/* Return whether TMPL is a (class template argument-) deducible template. */ + +bool +ctad_template_p (tree tmpl) +{ + /* A deducible template is either a class template or is an alias template + whose defining-type-id is of the form + + typename(opt) nested-name-specifier(opt) template(opt) simple-template-id + + where the nested-name-specifier (if any) is non-dependent and the + template-name of the simple-template-id names a deducible template. */ + + if (DECL_CLASS_TEMPLATE_P (tmpl) + || DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl)) + return true; + if (!DECL_ALIAS_TEMPLATE_P (tmpl)) + return false; + tree orig = DECL_ORIGINAL_TYPE (DECL_TEMPLATE_RESULT (tmpl)); + if (tree tinfo = get_template_info (orig)) + return ctad_template_p (TI_TEMPLATE (tinfo)); + return false; } /* Deduce template arguments for the class template placeholder PTYPE for @@ -28062,18 +28376,29 @@ is_spec_or_derived (tree etype, tree tmpl) type. */ static tree -do_class_deduction (tree ptype, tree tmpl, tree init, int flags, - tsubst_flags_t complain) +do_class_deduction (tree ptype, tree tmpl, tree init, + int flags, tsubst_flags_t complain) { - if (!DECL_CLASS_TEMPLATE_P (tmpl)) + /* We should have handled this in the caller. */ + if (DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl)) + return ptype; + + /* Look through alias templates that just rename another template. */ + tmpl = get_underlying_template (tmpl); + if (!ctad_template_p (tmpl)) { - /* We should have handled this in the caller. */ - if (DECL_TEMPLATE_TEMPLATE_PARM_P (tmpl)) - return ptype; if (complain & tf_error) - error ("non-class template %qT used without template arguments", tmpl); + error ("non-deducible template %qT used without template arguments", tmpl); return error_mark_node; } + else if (cxx_dialect < cxx2a && DECL_ALIAS_TEMPLATE_P (tmpl)) + { + /* This doesn't affect conforming C++17 code, so just pedwarn. */ + if (complain & tf_warning_or_error) + pedwarn (input_location, 0, "alias template deduction only available " + "with %<-std=c++2a%> or %<-std=gnu++2a%>"); + } + if (init && TREE_TYPE (init) == ptype) /* Using the template parm as its own argument. */ return ptype; @@ -28081,7 +28406,6 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, tree type = TREE_TYPE (tmpl); bool try_list_ctor = false; - bool copy_init = false; releasing_vec rv_args = NULL; vec *&args = *&rv_args; @@ -28089,7 +28413,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, args = make_tree_vector (); else if (BRACE_ENCLOSED_INITIALIZER_P (init)) { - if (CONSTRUCTOR_NELTS (init) == 1) + try_list_ctor = TYPE_HAS_LIST_CTOR (type); + if (try_list_ctor && CONSTRUCTOR_NELTS (init) == 1) { /* As an exception, the first phase in 16.3.1.7 (considering the initializer list as a single argument) is omitted if the @@ -28097,34 +28422,30 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, where U is a specialization of C or a class derived from a specialization of C. */ tree elt = CONSTRUCTOR_ELT (init, 0)->value; - copy_init = is_spec_or_derived (TREE_TYPE (elt), tmpl); + if (is_spec_or_derived (TREE_TYPE (elt), tmpl)) + try_list_ctor = false; } - try_list_ctor = !copy_init && TYPE_HAS_LIST_CTOR (type); if (try_list_ctor || is_std_init_list (type)) args = make_tree_vector_single (init); else args = make_tree_vector_from_ctor (init); } + else if (TREE_CODE (init) == TREE_LIST) + args = make_tree_vector_from_list (init); else - { - if (TREE_CODE (init) == TREE_LIST) - args = make_tree_vector_from_list (init); - else - args = make_tree_vector_single (init); + args = make_tree_vector_single (init); - if (args->length() == 1) - copy_init = is_spec_or_derived (TREE_TYPE ((*args)[0]), tmpl); - } + /* Do this now to avoid problems with erroneous args later on. */ + args = resolve_args (args, complain); + if (args == NULL) + return error_mark_node; - tree dname = dguide_name (tmpl); - tree cands = lookup_qualified_name (CP_DECL_CONTEXT (tmpl), dname, - /*type*/false, /*complain*/false, - /*hidden*/false); - bool elided = false; + tree cands = deduction_guides_for (tmpl, complain); if (cands == error_mark_node) - cands = NULL_TREE; + return error_mark_node; /* Prune explicit deduction guides in copy-initialization context. */ + bool elided = false; if (flags & LOOKUP_ONLYCONVERTING) { for (lkp_iterator iter (cands); !elided && iter; ++iter) @@ -28143,37 +28464,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, } } - tree outer_args = NULL_TREE; - if (DECL_CLASS_SCOPE_P (tmpl) - && CLASSTYPE_TEMPLATE_INSTANTIATION (DECL_CONTEXT (tmpl))) - { - outer_args = CLASSTYPE_TI_ARGS (DECL_CONTEXT (tmpl)); - type = TREE_TYPE (most_general_template (tmpl)); - } - - bool saw_ctor = false; - // FIXME cache artificial deduction guides - for (ovl_iterator iter (CLASSTYPE_CONSTRUCTORS (type)); iter; ++iter) - { - /* Skip inherited constructors. */ - if (iter.using_p ()) - continue; - - tree guide = build_deduction_guide (type, *iter, outer_args, complain); - if (guide == error_mark_node) - return error_mark_node; - if ((flags & LOOKUP_ONLYCONVERTING) - && DECL_NONCONVERTING_P (STRIP_TEMPLATE (guide))) - elided = true; - else - cands = lookup_add (guide, cands); - - saw_ctor = true; - } - - if (!copy_init) - if (tree guide = maybe_aggr_guide (type, init)) - cands = lookup_add (guide, cands); + if (tree guide = maybe_aggr_guide (tmpl, init, args)) + cands = lookup_add (guide, cands); tree call = error_mark_node; @@ -28202,28 +28494,6 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, } } - /* Maybe generate an implicit deduction guide. */ - if (call == error_mark_node && args->length () < 2) - { - tree gtype = NULL_TREE; - - if (args->length () == 1) - /* Generate a copy guide. */ - gtype = build_reference_type (type); - else if (!saw_ctor) - /* Generate a default guide. */ - gtype = type; - - if (gtype) - { - tree guide = build_deduction_guide (type, gtype, outer_args, - complain); - if (guide == error_mark_node) - return error_mark_node; - cands = lookup_add (guide, cands); - } - } - if (elided && !cands) { error ("cannot deduce template arguments for copy-initialization" @@ -28245,7 +28515,8 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags, --cp_unevaluated_operand; } - if (call == error_mark_node && (complain & tf_warning_or_error)) + if (call == error_mark_node + && (complain & tf_warning_or_error)) { error ("class template argument deduction failed:"); diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index f500ee61442..8817860b4d3 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -2338,6 +2338,9 @@ lookup_mark (tree ovl, bool val) tree lookup_add (tree fns, tree lookup) { + if (fns == error_mark_node || lookup == error_mark_node) + return error_mark_node; + if (lookup || TREE_CODE (fns) == TEMPLATE_DECL) { lookup = ovl_make (fns, lookup); diff --git a/gcc/testsuite/g++.dg/cpp1z/class-deduction46.C b/gcc/testsuite/g++.dg/cpp1z/class-deduction46.C index 513e16057af..e98573729f3 100644 --- a/gcc/testsuite/g++.dg/cpp1z/class-deduction46.C +++ b/gcc/testsuite/g++.dg/cpp1z/class-deduction46.C @@ -3,4 +3,4 @@ template struct C; template<> struct C { C(int, int) {} }; -auto k = C{0, 0}; // { dg-error "cannot deduce" } +auto k = C{0, 0}; // { dg-error "" } diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias1.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias1.C new file mode 100644 index 00000000000..ed47eb38f1b --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias1.C @@ -0,0 +1,27 @@ +// Testcase from P1814R0 +// { dg-do compile { target c++2a } } + +template struct identity { using type = T; }; +template using identity_t = typename identity::type; +template concept Int = __is_same_as (T, int); + +template struct C { + C(T, U); // #1 { dg-message "constraint" } +}; +template +C(T, U) -> C>; // #2 { dg-message "constraint" } + +template +using A = C; + +template +using B = A; + +int i{}; +double d{}; +A a1(&i, &i); // { dg-bogus "" "Deduces A" } +A a2(i, i); // { dg-error "" "cannot deduce V * from i" } +A a3(&i, &d); // { dg-error "" } #1: Cannot deduce (V*, V*) from (int *, double *) + // #2: Cannot deduce A from C +B b1(&i, &i); // { dg-bogus "" "Deduces B" } +B b2(&d, &d); // { dg-error "" "cannot deduce B from C" } diff --git a/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias2.C b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias2.C new file mode 100644 index 00000000000..d855f7d11ce --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/class-deduction-alias2.C @@ -0,0 +1,22 @@ +// Test that a non-template deduction guide that doesn't match the alias is +// ignored. +// { dg-do compile { target c++2a } } + +template struct identity { using type = T; }; +template using identity_t = typename identity::type; + +template struct C { + C(T, U); // #1 +}; + +C(char*, char*) -> C; // #3 + +template +using A = C; + +char c; +A a4 (&c, &c); // ignores #3 because C is not an A + +static_assert (__is_same_as(decltype(a4),A)); + +C c2 (&c, &c); // { dg-error "conversion" } deduces with #3 diff --git a/gcc/testsuite/g++.dg/cpp2a/explicit11.C b/gcc/testsuite/g++.dg/cpp2a/explicit11.C index ad1bed5b3f0..2df42cdfec5 100644 --- a/gcc/testsuite/g++.dg/cpp2a/explicit11.C +++ b/gcc/testsuite/g++.dg/cpp2a/explicit11.C @@ -9,7 +9,7 @@ struct A { }; int i; -A a1 = { i, i }; // { dg-error "deduction|cannot" } +A a1 = { i, i }; // { dg-error "deduction|cannot|no match" } A a2{ i, i }; A a3{ 0, i }; A a4 = { 0, i }; diff --git a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C index 753a6ecd0a8..9b6e2f59d2c 100644 --- a/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C +++ b/gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C @@ -358,8 +358,8 @@ #ifndef __cpp_deduction_guides # error "__cpp_deduction_guides" -#elif __cpp_deduction_guides != 201703 -# error "__cpp_deduction_guides != 201703" +#elif __cpp_deduction_guides != 201907 +# error "__cpp_deduction_guides != 201907" #endif #ifndef __cpp_if_constexpr