aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2019-11-27 22:05:53 +0000
committerJason Merrill <jason@redhat.com>2019-11-27 22:05:53 +0000
commitddcbf537ec3d1aefabb385527a67aa4bb4e6246d (patch)
tree3f4b4913ffbd60d6a8230235c945963f61958534
parentac1745ae600ec50d9248cc720559167ac10a0ecb (diff)
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. git-svn-id: https://gcc.gnu.org/svn/gcc/trunk@278786 138bc75d-0d04-0410-961f-82ee72b054a4
-rw-r--r--gcc/c-family/ChangeLog4
-rw-r--r--gcc/c-family/c-cppbuiltin.c4
-rw-r--r--gcc/cp/ChangeLog11
-rw-r--r--gcc/cp/call.c2
-rw-r--r--gcc/cp/constraint.cc19
-rw-r--r--gcc/cp/cp-tree.h2
-rw-r--r--gcc/cp/parser.c3
-rw-r--r--gcc/cp/pt.c481
-rw-r--r--gcc/cp/tree.c3
-rw-r--r--gcc/testsuite/g++.dg/cpp1z/class-deduction46.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/class-deduction-alias1.C27
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/class-deduction-alias2.C22
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/explicit11.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/feat-cxx2a.C4
14 files changed, 473 insertions, 113 deletions
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 <jason@redhat.com>
+
+ * c-cppbuiltin.c (c_cpp_builtins): Update __cpp_deduction_guides.
+
2019-11-26 Jakub Jelinek <jakub@redhat.com>
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,5 +1,16 @@
2019-11-16 Jason Merrill <jason@redhat.com>
+ 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 <jason@redhat.com>
+
* cxx-pretty-print.c (pp_cxx_unqualified_id): Handle alias
template-id.
* pt.c (complex_alias_template_p): True if constraints.
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<tree, va_gc> *
+vec<tree, va_gc> *
resolve_args (vec<tree, va_gc> *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<tree,va_gc> *resolve_args (vec<tree,va_gc>*, 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<tree,va_gc> *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<tree, tree_pair_p> *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<hm_ggc> (dguide_cache, tmpl))
+ {
+ cache = r;
+ if (cache->purpose == guides)
+ return cache->value;
+ }
+ else
+ {
+ r = cache = ggc_cleared_alloc<tree_pair_s> ();
+ 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<tree,va_gc> *&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<class S> struct C;
template<> struct C<int> { 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 <class T> struct identity { using type = T; };
+template <class T> using identity_t = typename identity<T>::type;
+template <class T> concept Int = __is_same_as (T, int);
+
+template <class T, class U> struct C {
+ C(T, U); // #1 { dg-message "constraint" }
+};
+template<class T, class U>
+C(T, U) -> C<T, identity_t<U>>; // #2 { dg-message "constraint" }
+
+template<class V>
+using A = C<V *, V *>;
+
+template<Int W>
+using B = A<W>;
+
+int i{};
+double d{};
+A a1(&i, &i); // { dg-bogus "" "Deduces A<int>" }
+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<V> from C<int *, double *>
+B b1(&i, &i); // { dg-bogus "" "Deduces B<int>" }
+B b2(&d, &d); // { dg-error "" "cannot deduce B<W> from C<double *, double *>" }
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 <class T> struct identity { using type = T; };
+template <class T> using identity_t = typename identity<T>::type;
+
+template <class T, class U> struct C {
+ C(T, U); // #1
+};
+
+C(char*, char*) -> C<int,int>; // #3
+
+template<class V>
+using A = C<V *, V *>;
+
+char c;
+A a4 (&c, &c); // ignores #3 because C<int,int> is not an A<V>
+
+static_assert (__is_same_as(decltype(a4),A<char>));
+
+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