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
This commit is contained in:
Jason Merrill 2017-02-28 18:57:09 -05:00 committed by Jason Merrill
parent ad1de65225
commit 853ef4e563
11 changed files with 245 additions and 166 deletions

View File

@ -1,3 +1,13 @@
2017-02-28 Jason Merrill <jason@redhat.com>
Class template argument deduction refinements
* call.c (joust): Move deduction guide tiebreaker down.
* decl.c (start_decl_1, cp_finish_decl, grokdeclarator): Allow class
deduction with no initializer.
* pt.c (build_deduction_guide): Handle implicit default/copy ctor.
(do_class_deduction): Use that rather than special case.
(do_auto_deduction): Handle null initializer.
2017-02-28 Jakub Jelinek <jakub@redhat.com>
* decl.c (find_decomp_class_base): Use cond ? G_("...") : G_("...")

View File

@ -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) */

View File

@ -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)
{

View File

@ -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);
}
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)
type = ctor;
bool copy_p = TREE_CODE (type) == REFERENCE_TYPE;
if (copy_p)
{
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);
type = TREE_TYPE (type);
fparms = tree_cons (NULL_TREE, type, void_list_node);
}
else
fparms = void_list_node;
/* 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;
tree ctmpl = CLASSTYPE_TI_TEMPLATE (type);
tparms = DECL_TEMPLATE_PARMS (ctmpl);
targs = CLASSTYPE_TI_ARGS (type);
ci = NULL_TREE;
fargs = NULL_TREE;
loc = DECL_SOURCE_LOCATION (ctmpl);
explicit_p = false;
}
else
{
if (outer_args)
ctor = tsubst (ctor, outer_args, complain, ctor);
type = DECL_CONTEXT (ctor);
tree fn_tmpl;
if (TREE_CODE (ctor) == TEMPLATE_DECL)
{
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);
current_template_parms = save_parms;
--processing_template_decl;
}
}
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<tree,va_gc> *args;
if (TREE_CODE (init) == TREE_LIST)
if (init == NULL_TREE
|| TREE_CODE (init) == TREE_LIST)
args = make_tree_vector_from_list (init);
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. */

View File

@ -0,0 +1,11 @@
// { dg-options -std=c++1z }
#include <initializer_list>
template <class T>
struct A
{
A (std::initializer_list<T>);
};
A a{1,2};

View File

@ -15,10 +15,10 @@ template<class T> struct A {
template<class T, int N = remove_ref_t<T>::value> A(T&&, int*) -> A<T>; //#3
A a{1,0}; // uses #1 to deduce A<int> and initializes with #1
A b{a,0}; // uses #3 (not #2) to deduce A<A<int>&> and initializes with #1
A b{a,0}; // uses #2 to deduce A<int> and initializes with #2
template <class,class> struct same;
template <class T> struct same<T,T> {};
same<decltype(a),A<int>> s1;
same<decltype(b),A<A<int>&>> s2;
same<decltype(b),A<int>> s2;

View File

@ -3,4 +3,4 @@
template <class T = void> struct A { };
A a{};
A a2;

View File

@ -0,0 +1,22 @@
// { dg-options -std=c++1z }
template <class T> struct A {
A(T); // #1
A(const A&); // #2
};
template <class T> A(T) -> A<T>; // #3
A a (42); // uses #3 to deduce A<int> and initializes with #1
A b = a; // uses #2 (not #3) to deduce A<int> and initializes with #2; #2 is more specialized
template <class T> A(A<T>) -> A<A<T>>; // #4
A b2 = a; // uses #4 to deduce A<A<int>> and initializes with #1; #4 is as specialized as #2
template <class,class> struct same;
template <class T> struct same<T,T> {};
same<decltype(a),A<int>> s1;
same<decltype(b),A<int>> s2;
same<decltype(b2),A<A<int>>> s3;

View File

@ -0,0 +1,5 @@
// { dg-options -std=c++1z }
#include <initializer_list>
std::initializer_list l { 1, 2, 3 };

View File

@ -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 <int>
@ -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 }

View File

@ -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;
}