commit db58204eb51c1a6554ca291927049175d02bf9ce Author: Jason Merrill Date: Tue Jun 1 22:23:00 2010 -0400 PR c++/44358 * call.c (build_list_conv): Set list-initialization flags properly. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 64e2e50..4062702 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -589,6 +589,12 @@ build_list_conv (tree type, tree ctor, int flags) unsigned i; tree val; + /* Within a list-initialization we can have more user-defined + conversions. */ + flags &= ~LOOKUP_NO_CONVERSION; + /* But no narrowing conversions. */ + flags |= LOOKUP_NO_NARROWING; + FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (ctor), i, val) { conversion *sub diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist36.C b/gcc/testsuite/g++.dg/cpp0x/initlist36.C new file mode 100644 index 0000000..a3305e1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/initlist36.C @@ -0,0 +1,21 @@ +// PR c++/44358 +// { dg-options "-std=c++0x" } + +#include + +struct A +{ + A(int); +}; + +struct B +{ + B(std::initializer_list); +}; + +void f (B b); +int main() +{ + B b0 = {{1}}; + B b1 = {{1.0}}; // { dg-error "narrowing" } +} commit 5f71dbceab6a8e369905679f77d3b34f0b8dba0c Author: Jason Merrill Date: Tue Jun 1 22:25:17 2010 -0400 DR 990 * call.c (add_list_candidates): Prefer the default constructor. (build_aggr_conv): Treat missing initializers like { }. * typeck2.c (process_init_constructor_record): Likewise. * init.c (expand_default_init): Use digest_init for direct aggregate initialization, too. * call.c (add_list_candidates): Split out... (build_user_type_conversion_1): ...from here. (build_new_method_call): And here. (implicit_conversion): Propagate LOOKUP_NO_NARROWING. diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 4062702..b9f1c7f 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -634,6 +634,7 @@ build_aggr_conv (tree type, tree ctor, int flags) unsigned HOST_WIDE_INT i = 0; conversion *c; tree field = next_initializable_field (TYPE_FIELDS (type)); + tree empty_ctor = NULL_TREE; for (; field; field = next_initializable_field (TREE_CHAIN (field))) { @@ -647,8 +648,14 @@ build_aggr_conv (tree type, tree ctor, int flags) if (TREE_CODE (type) == UNION_TYPE) break; } - else if (build_value_init (TREE_TYPE (field)) == error_mark_node) - return NULL; + else + { + if (empty_ctor == NULL_TREE) + empty_ctor = build_constructor (init_list_type_node, NULL); + if (!can_convert_arg (TREE_TYPE (field), TREE_TYPE (empty_ctor), + empty_ctor, flags)) + return NULL; + } } if (i < CONSTRUCTOR_NELTS (ctor)) @@ -1461,7 +1468,8 @@ implicit_conversion (tree to, tree from, tree expr, bool c_cast_p, && (flags & LOOKUP_NO_CONVERSION) == 0) { struct z_candidate *cand; - int convflags = (flags & (LOOKUP_NO_TEMP_BIND|LOOKUP_ONLYCONVERTING)); + int convflags = (flags & (LOOKUP_NO_TEMP_BIND|LOOKUP_ONLYCONVERTING + |LOOKUP_NO_NARROWING)); if (CLASS_TYPE_P (to) && !CLASSTYPE_NON_AGGREGATE (complete_type (to)) @@ -2813,6 +2821,65 @@ merge_conversion_sequences (conversion *user_seq, conversion *std_seq) return std_seq; } +/* Handle overload resolution for initializing an object of class type from + an initializer list. First we look for a suitable constructor that + takes a std::initializer_list; if we don't find one, we then look for a + non-list constructor. + + Parameters are as for add_candidates, except that the arguments are in + the form of a CONSTRUCTOR (the initializer list) rather than a VEC, and + the RETURN_TYPE parameter is replaced by TOTYPE, the desired type. */ + +static void +add_list_candidates (tree fns, tree first_arg, + tree init_list, tree totype, + tree explicit_targs, bool template_only, + tree conversion_path, tree access_path, + int flags, + struct z_candidate **candidates) +{ + VEC(tree,gc) *args; + + gcc_assert (*candidates == NULL); + + /* For list-initialization we consider explicit constructors, but + give an error if one is selected. */ + flags &= ~LOOKUP_ONLYCONVERTING; + /* And we don't allow narrowing conversions. We also use this flag to + avoid the copy constructor call for copy-list-initialization. */ + flags |= LOOKUP_NO_NARROWING; + + /* Always use the default constructor if the list is empty (DR 990). */ + if (CONSTRUCTOR_NELTS (init_list) == 0 + && TYPE_HAS_DEFAULT_CONSTRUCTOR (totype)) + ; + /* If the class has a list ctor, try passing the list as a single + argument first, but only consider list ctors. */ + else if (TYPE_HAS_LIST_CTOR (totype)) + { + flags |= LOOKUP_LIST_ONLY; + args = make_tree_vector_single (init_list); + add_candidates (fns, first_arg, args, NULL_TREE, + explicit_targs, template_only, conversion_path, + access_path, flags, candidates); + if (any_strictly_viable (*candidates)) + return; + } + + args = ctor_to_vec (init_list); + + /* We aren't looking for list-ctors anymore. */ + flags &= ~LOOKUP_LIST_ONLY; + /* We allow more user-defined conversions within an init-list. */ + flags &= ~LOOKUP_NO_CONVERSION; + /* But not for the copy ctor. */ + flags |= LOOKUP_NO_COPY_CTOR_CONVERSION; + + add_candidates (fns, first_arg, args, NULL_TREE, + explicit_targs, template_only, conversion_path, + access_path, flags, candidates); +} + /* Returns the best overload candidate to perform the requested conversion. This function is used for three the overloading situations described in [over.match.copy], [over.match.conv], and [over.match.ref]. @@ -2872,49 +2939,25 @@ build_user_type_conversion_1 (tree totype, tree expr, int flags) if (ctors) { int ctorflags = flags; - bool try_single_arg = true; ctors = BASELINK_FUNCTIONS (ctors); first_arg = build_int_cst (build_pointer_type (totype), 0); - if (BRACE_ENCLOSED_INITIALIZER_P (expr)) - { - /* For list-initialization we consider explicit constructors, but - give an error if one is selected. */ - ctorflags &= ~LOOKUP_ONLYCONVERTING; - /* If the class has a list ctor, try passing the list as a single - argument first, but only consider list ctors. */ - if (TYPE_HAS_LIST_CTOR (totype)) - ctorflags |= LOOKUP_LIST_ONLY; - else - try_single_arg = false; - } /* We should never try to call the abstract or base constructor from here. */ gcc_assert (!DECL_HAS_IN_CHARGE_PARM_P (OVL_CURRENT (ctors)) && !DECL_HAS_VTT_PARM_P (OVL_CURRENT (ctors))); - /* If EXPR is not an initializer-list, or if totype has a list - constructor, try EXPR as a single argument. */ - if (try_single_arg) + if (BRACE_ENCLOSED_INITIALIZER_P (expr)) { - args = make_tree_vector_single (expr); - add_candidates (ctors, first_arg, args, NULL_TREE, NULL_TREE, false, - TYPE_BINFO (totype), TYPE_BINFO (totype), - ctorflags, &candidates); + /* List-initialization. */ + add_list_candidates (ctors, first_arg, expr, totype, NULL_TREE, + false, TYPE_BINFO (totype), TYPE_BINFO (totype), + ctorflags, &candidates); } - - /* If we didn't find a suitable list constructor for an initializer-list, - try breaking it apart. */ - if (!candidates && BRACE_ENCLOSED_INITIALIZER_P (expr)) + else { - args = ctor_to_vec (expr); - /* We aren't looking for list-ctors anymore. */ - ctorflags &= ~LOOKUP_LIST_ONLY; - /* We still allow more conversions within an init-list. */ - ctorflags &= ~LOOKUP_NO_CONVERSION; - /* But not for the copy ctor. */ - ctorflags |= LOOKUP_NO_COPY_CTOR_CONVERSION; + args = make_tree_vector_single (expr); add_candidates (ctors, first_arg, args, NULL_TREE, NULL_TREE, false, TYPE_BINFO (totype), TYPE_BINFO (totype), ctorflags, &candidates); @@ -6233,8 +6276,6 @@ build_new_method_call (tree instance, tree fns, VEC(tree,gc) **args, tree orig_fns; VEC(tree,gc) *orig_args = NULL; void *p; - tree list = NULL_TREE; - bool try_normal; gcc_assert (instance != NULL_TREE); @@ -6346,47 +6387,32 @@ build_new_method_call (tree instance, tree fns, VEC(tree,gc) **args, if (DECL_DESTRUCTOR_P (fn)) name = complete_dtor_identifier; + first_mem_arg = instance_ptr; + + /* Get the high-water mark for the CONVERSION_OBSTACK. */ + p = conversion_obstack_alloc (0); + /* If CONSTRUCTOR_IS_DIRECT_INIT is set, this was a T{ } form - initializer, not T({ }). If the type doesn't have a list ctor (or no - viable list ctor), break apart the list into separate ctor args. */ - try_normal = true; + initializer, not T({ }). */ if (DECL_CONSTRUCTOR_P (fn) && args != NULL && !VEC_empty (tree, *args) && BRACE_ENCLOSED_INITIALIZER_P (VEC_index (tree, *args, 0)) && CONSTRUCTOR_IS_DIRECT_INIT (VEC_index (tree, *args, 0))) { gcc_assert (VEC_length (tree, *args) == 1 && !(flags & LOOKUP_ONLYCONVERTING)); - list = VEC_index (tree, *args, 0); - if (TYPE_HAS_LIST_CTOR (basetype)) - flags |= LOOKUP_LIST_ONLY; - else - try_normal = false; + add_list_candidates (fns, first_mem_arg, VEC_index (tree, *args, 0), + basetype, explicit_targs, template_only, + conversion_path, access_binfo, flags, &candidates); } - - first_mem_arg = instance_ptr; - - /* Get the high-water mark for the CONVERSION_OBSTACK. */ - p = conversion_obstack_alloc (0); - - any_viable_p = false; - if (try_normal) + else { add_candidates (fns, first_mem_arg, user_args, optype, explicit_targs, template_only, conversion_path, access_binfo, flags, &candidates); - candidates = splice_viable (candidates, pedantic, &any_viable_p); - } - - if (!any_viable_p && list) - { - VEC(tree,gc) *list_args = ctor_to_vec (list); - flags &= ~LOOKUP_LIST_ONLY; - add_candidates (fns, first_mem_arg, list_args, optype, - explicit_targs, template_only, conversion_path, - access_binfo, flags, &candidates); - candidates = splice_viable (candidates, pedantic, &any_viable_p); } + any_viable_p = false; + candidates = splice_viable (candidates, pedantic, &any_viable_p); if (!any_viable_p) { diff --git a/gcc/cp/init.c b/gcc/cp/init.c index 3e56417..1f3e803 100644 --- a/gcc/cp/init.c +++ b/gcc/cp/init.c @@ -1308,6 +1308,18 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags, tree rval; VEC(tree,gc) *parms; + if (init && BRACE_ENCLOSED_INITIALIZER_P (init) + && CP_AGGREGATE_TYPE_P (type)) + { + /* A brace-enclosed initializer for an aggregate. In C++0x this can + happen for direct-initialization, too. */ + init = digest_init (type, init); + init = build2 (INIT_EXPR, TREE_TYPE (exp), exp, init); + TREE_SIDE_EFFECTS (init) = 1; + finish_expr_stmt (init); + return; + } + if (init && TREE_CODE (init) != TREE_LIST && (flags & LOOKUP_ONLYCONVERTING)) { @@ -1320,12 +1332,6 @@ expand_default_init (tree binfo, tree true_exp, tree exp, tree init, int flags, to run a new constructor; and catching an exception, where we have already built up the constructor call so we could wrap it in an exception region. */; - else if (BRACE_ENCLOSED_INITIALIZER_P (init) - && CP_AGGREGATE_TYPE_P (type)) - { - /* A brace-enclosed initializer for an aggregate. */ - init = digest_init (type, init); - } else init = ocp_convert (type, init, CONV_IMPLICIT|CONV_FORCE_TEMP, flags); diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index 489d3f8..7603ead 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -1165,17 +1165,15 @@ process_init_constructor_record (tree type, tree init) default-initialization, we can't rely on the back end to do it for us, so build up TARGET_EXPRs. If the type in question is a class, just build one up; if it's an array, recurse. */ + next = build_constructor (init_list_type_node, NULL); if (MAYBE_CLASS_TYPE_P (TREE_TYPE (field))) { - next = build_functional_cast (TREE_TYPE (field), NULL_TREE, - tf_warning_or_error); + next = finish_compound_literal (TREE_TYPE (field), next); /* direct-initialize the target. No temporary is going to be involved. */ if (TREE_CODE (next) == TARGET_EXPR) TARGET_EXPR_DIRECT_INIT_P (next) = true; } - else - next = build_constructor (init_list_type_node, NULL); next = digest_init_r (TREE_TYPE (field), next, true, LOOKUP_IMPLICIT); diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist36.C b/gcc/testsuite/g++.dg/cpp0x/initlist36.C index a3305e1..94624c9 100644 --- a/gcc/testsuite/g++.dg/cpp0x/initlist36.C +++ b/gcc/testsuite/g++.dg/cpp0x/initlist36.C @@ -18,4 +18,6 @@ int main() { B b0 = {{1}}; B b1 = {{1.0}}; // { dg-error "narrowing" } + B b2 {1.0}; // { dg-error "narrowing" } + A a {1.0}; // { dg-error "narrowing" } } diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist37.C b/gcc/testsuite/g++.dg/cpp0x/initlist37.C new file mode 100644 index 0000000..20c6ab6 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/initlist37.C @@ -0,0 +1,24 @@ +// DR 990 +// { dg-options "-std=c++0x" } + +#include + +struct S { + S(std::initializer_list); // #1 + S(std::initializer_list); // #2 + S(); // #3 + // ... +}; +S s1 = { 1.0, 2.0, 3.0 }; // invoke #1 +S s2 = { 1, 2, 3 }; // invoke #2 +S s3 = { }; // invoke #3 (for value-initialization) + + +// Test some other situations, too. +void f (S); +int main() +{ + S s4 { }; + f({ }); + S {}; +} diff --git a/gcc/testsuite/g++.dg/cpp0x/initlist38.C b/gcc/testsuite/g++.dg/cpp0x/initlist38.C new file mode 100644 index 0000000..818d69a --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp0x/initlist38.C @@ -0,0 +1,21 @@ +// DR 990 +// { dg-options "-std=c++0x" } + +#include + +struct A { + A(std::initializer_list); // #1 +}; +struct B { + A a; +}; + +void f (B); +int main() +{ + B{}; + f({}); + B b0 = { }; + B b1 { }; // OK, uses #1 + B b2 { 1 }; // { dg-error "conversion" } +}