Commit 853ef4e5 by Jason Merrill Committed by Jason Merrill

Class template argument deduction refinements

	* call.c (joust): Move deduction guide tiebreaker down.
	* decl.c (start_decl_1, cp_finish_decl, grokdeclarator): Allow class
	deduction with no initializer.
	* pt.c (build_deduction_guide): Handle implicit default/copy ctor.
	(do_class_deduction): Use that rather than special case.
	(do_auto_deduction): Handle null initializer.

From-SVN: r245796
parent ad1de652
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_("...")
......
......@@ -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) */
......
......@@ -5238,14 +5238,16 @@ 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;
}
}
/* Create a new scope to hold this declaration if necessary.
Whether or not a new scope is necessary cannot be determined
......@@ -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 (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,16 +12328,9 @@ grokdeclarator (const cp_declarator *declarator,
if (VAR_P (decl) && !initialized)
if (tree auto_node = type_uses_auto (type))
if (!CLASS_PLACEHOLDER_TEMPLATE (auto_node))
{
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
error_at (loc, "declaration of %q#D has no initializer", decl);
TREE_TYPE (decl) = error_mark_node;
}
......
......@@ -24941,14 +24941,42 @@ 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)
{
tree type, tparms, targs, fparms, fargs, ci;
bool memtmpl = false;
bool explicit_p;
location_t loc;
if (TYPE_P (ctor))
{
type = ctor;
bool copy_p = TREE_CODE (type) == REFERENCE_TYPE;
if (copy_p)
{
type = TREE_TYPE (type);
fparms = tree_cons (NULL_TREE, type, void_list_node);
}
else
fparms = void_list_node;
tree ctmpl = CLASSTYPE_TI_TEMPLATE (type);
tparms = DECL_TEMPLATE_PARMS (ctmpl);
targs = CLASSTYPE_TI_ARGS (type);
ci = NULL_TREE;
fargs = NULL_TREE;
loc = DECL_SOURCE_LOCATION (ctmpl);
explicit_p = false;
}
else
{
if (outer_args)
ctor = tsubst (ctor, outer_args, complain, ctor);
tree type = DECL_CONTEXT (ctor);
type = DECL_CONTEXT (ctor);
tree fn_tmpl;
if (TREE_CODE (ctor) == TEMPLATE_DECL)
{
......@@ -24958,34 +24986,38 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
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),
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. */
tree fparms = FUNCTION_ARG_CHAIN (ctor);
tree fargs = TREE_CHAIN (DECL_ARGUMENTS (ctor));
tree ci = get_constraints (ctor);
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))
{
/* 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. */
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. */
/* 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. */
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));
......@@ -24997,8 +25029,8 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
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
/* 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);
......@@ -25037,7 +25069,9 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
current_template_parms = save_parms;
--processing_template_decl;
}
else
}
if (!memtmpl)
{
/* Copy the parms so we can set DECL_PRIMARY_TEMPLATE. */
tparms = copy_node (tparms);
......@@ -25046,12 +25080,12 @@ build_deduction_guide (tree ctor, tree outer_args, tsubst_flags_t complain)
}
tree fntype = build_function_type (type, fparms);
tree ded_fn = build_lang_decl_loc (DECL_SOURCE_LOCATION (ctor),
tree ded_fn = build_lang_decl_loc (loc,
FUNCTION_DECL,
dguide_name (type), fntype);
DECL_ARGUMENTS (ded_fn) = fargs;
DECL_ARTIFICIAL (ded_fn) = true;
DECL_NONCONVERTING_P (ded_fn) = DECL_NONCONVERTING_P (ctor);
DECL_NONCONVERTING_P (ded_fn) = explicit_p;
tree ded_tmpl = build_template_decl (ded_fn, tparms, /*member*/false);
DECL_ARTIFICIAL (ded_tmpl) = true;
DECL_TEMPLATE_RESULT (ded_tmpl) = ded_fn;
......@@ -25085,27 +25119,16 @@ do_class_deduction (tree ptype, tree tmpl, tree init, int flags,
tree type = TREE_TYPE (tmpl);
vec<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);
}
if (cands == NULL_TREE)
tree parms = FUNCTION_FIRST_USER_PARMTYPE (fn);
if (sufficient_parms_p (parms))
saw_default = true;
if (parms && sufficient_parms_p (TREE_CHAIN (parms)))
{
if (args->length() == 0)
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 (!saw_default && 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;
tree guide = build_deduction_guide (type, outer_args, complain);
cands = ovl_cons (guide, cands);
}
error ("cannot deduce template arguments for %qT, as it has "
"no deduction guides or user-declared constructors", type);
return error_mark_node;
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. */
......
// { dg-options -std=c++1z }
#include <initializer_list>
template <class T>
struct A
{
A (std::initializer_list<T>);
};
A a{1,2};
......@@ -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;
......@@ -3,4 +3,4 @@
template <class T = void> struct A { };
A a{};
A a2;
// { 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;
// { dg-options -std=c++1z }
#include <initializer_list>
std::initializer_list l { 1, 2, 3 };
......@@ -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 }
......@@ -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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment