diff options
author | Marek Polacek <polacek@redhat.com> | 2019-12-03 15:59:40 +0000 |
---|---|---|
committer | Marek Polacek <mpolacek@gcc.gnu.org> | 2019-12-03 15:59:40 +0000 |
commit | 43aae289866f5ea55d187444520412554aa2e171 (patch) | |
tree | a3a517242014a3db820710f763749398c1d1e0f0 /gcc/cp | |
parent | 577f4a0e5e7f7ef9b5729a3eed79e523cba9dfa9 (diff) | |
download | gcc-43aae289866f5ea55d187444520412554aa2e171.zip gcc-43aae289866f5ea55d187444520412554aa2e171.tar.gz gcc-43aae289866f5ea55d187444520412554aa2e171.tar.bz2 |
PR c++/91363 - P0960R3: Parenthesized initialization of aggregates.
This patch implements C++20 P0960R3: Parenthesized initialization of aggregates
(<wg21.link/p0960>; see R0 for more background info). Essentially, if you have
an aggregate, you can now initialize it by (x, y), similarly to {x, y}. E.g.
struct A {
int x, y;
// no A(int, int) ctor (see paren-init14.C for = delete; case)
};
A a(1, 2);
The difference between ()-init and {}-init is that narrowing conversions are
permitted, designators are not permitted, a temporary object bound to
a reference does not have its lifetime extended, and there is no brace elision.
Further, things like
int a[](1, 2, 3); // will deduce the array size
const A& r(1, 2.3, 3); // narrowing is OK
int (&&rr)[](1, 2, 3);
int b[3](1, 2); // b[2] will be value-initialized
now work as expected. Note that
char f[]("fluff");
has always worked and this patch keeps it that way. Also note that A a((1, 2))
is not the same as A a{{1,2}}; the inner (1, 2) remains a COMPOUND_EXPR.
The approach I took was to handle (1, 2) similarly to {1, 2} -- conjure up
a CONSTRUCTOR, and introduce LOOKUP_AGGREGATE_PAREN_INIT to distinguish
between the two. This kind of initialization is only supported in C++20;
I've made no attempt to support it in earlier standards, like we don't
support CTAD pre-C++17, for instance.
* c-cppbuiltin.c (c_cpp_builtins): Predefine
__cpp_aggregate_paren_init=201902 for -std=c++2a.
* call.c (build_new_method_call_1): Handle parenthesized initialization
of aggregates by building up a CONSTRUCTOR.
(extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
* cp-tree.h (CONSTRUCTOR_IS_PAREN_INIT, LOOKUP_AGGREGATE_PAREN_INIT):
Define.
* decl.c (grok_reference_init): Handle aggregate initialization from
a parenthesized list of values.
(reshape_init): Do nothing for CONSTRUCTOR_IS_PAREN_INIT.
(check_initializer): Handle initialization of an array from a
parenthesized list of values. Use NULL_TREE instead of NULL.
* tree.c (build_cplus_new): Handle BRACE_ENCLOSED_INITIALIZER_P.
* typeck2.c (digest_init_r): Set LOOKUP_AGGREGATE_PAREN_INIT if it
receives a CONSTRUCTOR with CONSTRUCTOR_IS_PAREN_INIT set. Allow
narrowing when LOOKUP_AGGREGATE_PAREN_INIT.
(massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing
flags to digest_init_r.
* g++.dg/cpp0x/constexpr-99.C: Only expect an error in C++17 and
lesser.
* g++.dg/cpp0x/explicit7.C: Likewise.
* g++.dg/cpp0x/initlist12.C: Adjust dg-error.
* g++.dg/cpp0x/pr31437.C: Likewise.
* g++.dg/cpp2a/feat-cxx2a.C: Add __cpp_aggregate_paren_init test.
* g++.dg/cpp2a/paren-init1.C: New test.
* g++.dg/cpp2a/paren-init10.C: New test.
* g++.dg/cpp2a/paren-init11.C: New test.
* g++.dg/cpp2a/paren-init12.C: New test.
* g++.dg/cpp2a/paren-init13.C: New test.
* g++.dg/cpp2a/paren-init14.C: New test.
* g++.dg/cpp2a/paren-init15.C: New test.
* g++.dg/cpp2a/paren-init16.C: New test.
* g++.dg/cpp2a/paren-init17.C: New test.
* g++.dg/cpp2a/paren-init18.C: New test.
* g++.dg/cpp2a/paren-init19.C: New test.
* g++.dg/cpp2a/paren-init2.C: New test.
* g++.dg/cpp2a/paren-init3.C: New test.
* g++.dg/cpp2a/paren-init4.C: New test.
* g++.dg/cpp2a/paren-init5.C: New test.
* g++.dg/cpp2a/paren-init6.C: New test.
* g++.dg/cpp2a/paren-init7.C: New test.
* g++.dg/cpp2a/paren-init8.C: New test.
* g++.dg/cpp2a/paren-init9.C: New test.
* g++.dg/ext/desig10.C: Adjust dg-error.
* g++.dg/template/crash107.C: Likewise.
* g++.dg/template/crash95.C: Likewise.
* g++.old-deja/g++.jason/crash3.C: Likewise.
* g++.old-deja/g++.law/ctors11.C: Likewise.
* g++.old-deja/g++.law/ctors9.C: Likewise.
* g++.old-deja/g++.mike/net22.C: Likewise.
* g++.old-deja/g++.niklas/t128.C: Likewise.
From-SVN: r278939
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/ChangeLog | 20 | ||||
-rw-r--r-- | gcc/cp/call.c | 51 | ||||
-rw-r--r-- | gcc/cp/cp-tree.h | 7 | ||||
-rw-r--r-- | gcc/cp/decl.c | 68 | ||||
-rw-r--r-- | gcc/cp/tree.c | 9 | ||||
-rw-r--r-- | gcc/cp/typeck2.c | 17 |
6 files changed, 161 insertions, 11 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index 7582e1f..c29da7b 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,23 @@ +2019-12-03 Marek Polacek <polacek@redhat.com> + + PR c++/91363 - P0960R3: Parenthesized initialization of aggregates. + * call.c (build_new_method_call_1): Handle parenthesized initialization + of aggregates by building up a CONSTRUCTOR. + (extend_ref_init_temps): Do nothing for CONSTRUCTOR_IS_PAREN_INIT. + * cp-tree.h (CONSTRUCTOR_IS_PAREN_INIT, LOOKUP_AGGREGATE_PAREN_INIT): + Define. + * decl.c (grok_reference_init): Handle aggregate initialization from + a parenthesized list of values. + (reshape_init): Do nothing for CONSTRUCTOR_IS_PAREN_INIT. + (check_initializer): Handle initialization of an array from a + parenthesized list of values. Use NULL_TREE instead of NULL. + * tree.c (build_cplus_new): Handle BRACE_ENCLOSED_INITIALIZER_P. + * typeck2.c (digest_init_r): Set LOOKUP_AGGREGATE_PAREN_INIT if it + receives a CONSTRUCTOR with CONSTRUCTOR_IS_PAREN_INIT set. Allow + narrowing when LOOKUP_AGGREGATE_PAREN_INIT. + (massage_init_elt): Don't lose LOOKUP_AGGREGATE_PAREN_INIT when passing + flags to digest_init_r. + 2019-12-03 Jakub Jelinek <jakub@redhat.com> PR c++/92732 diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 062cff4..ea0e8b7 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -10124,6 +10124,38 @@ build_new_method_call_1 (tree instance, tree fns, vec<tree, va_gc> **args, if (!any_viable_p) { + /* [dcl.init], 17.6.2.2: + + Otherwise, if no constructor is viable, the destination type is + a (possibly cv-qualified) aggregate class A, and the initializer + is a parenthesized expression-list, the object is initialized as + follows... + + We achieve this by building up a CONSTRUCTOR, as for list-init, + and setting CONSTRUCTOR_IS_PAREN_INIT to distinguish between + the two. */ + if (DECL_CONSTRUCTOR_P (fn) + && !(flags & LOOKUP_ONLYCONVERTING) + && !cp_unevaluated_operand + && cxx_dialect >= cxx2a + && CP_AGGREGATE_TYPE_P (basetype) + && !user_args->is_empty ()) + { + /* Create a CONSTRUCTOR from ARGS, e.g. {1, 2} from <1, 2>. */ + tree list = build_tree_list_vec (user_args); + tree ctor = build_constructor_from_list (init_list_type_node, list); + CONSTRUCTOR_IS_DIRECT_INIT (ctor) = true; + CONSTRUCTOR_IS_PAREN_INIT (ctor) = true; + if (is_dummy_object (instance)) + return ctor; + else + { + ctor = digest_init (basetype, ctor, complain); + ctor = build2 (INIT_EXPR, TREE_TYPE (instance), instance, ctor); + TREE_SIDE_EFFECTS (ctor) = true; + return ctor; + } + } if (complain & tf_error) complain_about_no_candidates_for_method_call (instance, candidates, explicit_targs, basetype, @@ -11789,9 +11821,16 @@ perform_direct_initialization_if_possible (tree type, If the destination type is a (possibly cv-qualified) class type: -- If the initialization is direct-initialization ..., - constructors are considered. ... If no constructor applies, or - the overload resolution is ambiguous, the initialization is - ill-formed. */ + constructors are considered. + + -- If overload resolution is successful, the selected constructor + is called to initialize the object, with the initializer expression + or expression-list as its argument(s). + + -- Otherwise, if no constructor is viable, the destination type is + a (possibly cv-qualified) aggregate class A, and the initializer is + a parenthesized expression-list, the object is initialized as + follows... */ if (CLASS_TYPE_P (type)) { releasing_vec args (make_tree_vector_single (expr)); @@ -12147,6 +12186,12 @@ extend_ref_init_temps (tree decl, tree init, vec<tree, va_gc> **cleanups) ctor = TARGET_EXPR_INITIAL (ctor); if (TREE_CODE (ctor) == CONSTRUCTOR) { + /* [dcl.init] When initializing an aggregate from a parenthesized list + of values... a temporary object bound to a reference does not have + its lifetime extended. */ + if (CONSTRUCTOR_IS_PAREN_INIT (ctor)) + return init; + if (is_std_init_list (type)) { /* The temporary array underlying a std::initializer_list diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h index 7e810b8..4af18b0 100644 --- a/gcc/cp/cp-tree.h +++ b/gcc/cp/cp-tree.h @@ -4325,6 +4325,11 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter) #define CONSTRUCTOR_IS_DESIGNATED_INIT(NODE) \ (TREE_LANG_FLAG_6 (CONSTRUCTOR_CHECK (NODE))) +/* True if this CONSTRUCTOR comes from a parenthesized list of values, e.g. + A(1, 2, 3). */ +#define CONSTRUCTOR_IS_PAREN_INIT(NODE) \ + (CONSTRUCTOR_CHECK(NODE)->base.private_flag) + /* True if NODE represents a conversion for direct-initialization in a template. Set by perform_implicit_conversion_flags. */ #define IMPLICIT_CONV_EXPR_DIRECT_INIT(NODE) \ @@ -5583,6 +5588,8 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG }; args), then we swap the conversions back in build_new_op_1 (so they correspond to the order of the args in the candidate). */ #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1) +/* We're initializing an aggregate from a parenthesized list of values. */ +#define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1) #define LOOKUP_NAMESPACES_ONLY(F) \ (((F) & LOOKUP_PREFER_NAMESPACES) && !((F) & LOOKUP_PREFER_TYPES)) diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index 81d7343..54f0950 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -5531,11 +5531,28 @@ grok_reference_init (tree decl, tree type, tree init, int flags) return NULL_TREE; } + tree ttype = TREE_TYPE (type); if (TREE_CODE (init) == TREE_LIST) - init = build_x_compound_expr_from_list (init, ELK_INIT, - tf_warning_or_error); + { + /* This handles (C++20 only) code like + + const A& r(1, 2, 3); + + where we treat the parenthesized list as a CONSTRUCTOR. */ + if (TREE_TYPE (init) == NULL_TREE + && CP_AGGREGATE_TYPE_P (ttype) + && !DECL_DECOMPOSITION_P (decl) + && (cxx_dialect >= cxx2a)) + { + init = build_constructor_from_list (init_list_type_node, init); + CONSTRUCTOR_IS_DIRECT_INIT (init) = true; + CONSTRUCTOR_IS_PAREN_INIT (init) = true; + } + else + init = build_x_compound_expr_from_list (init, ELK_INIT, + tf_warning_or_error); + } - tree ttype = TREE_TYPE (type); if (TREE_CODE (ttype) != ARRAY_TYPE && TREE_CODE (TREE_TYPE (init)) == ARRAY_TYPE) /* Note: default conversion is only called in very special cases. */ @@ -6437,6 +6454,11 @@ reshape_init (tree type, tree init, tsubst_flags_t complain) if (vec_safe_is_empty (v)) return init; + /* Brace elision is not performed for a CONSTRUCTOR representing + parenthesized aggregate initialization. */ + if (CONSTRUCTOR_IS_PAREN_INIT (init)) + return init; + /* Handle [dcl.init.list] direct-list-initialization from single element of enumeration with a fixed underlying type. */ if (is_direct_enum_init (type, init)) @@ -6640,6 +6662,41 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups) flags |= LOOKUP_NO_NARROWING; } } + /* [dcl.init] "Otherwise, if the destination type is an array, the object + is initialized as follows..." So handle things like + + int a[](1, 2, 3); + + which is permitted in C++20 by P0960. */ + else if (TREE_CODE (init) == TREE_LIST + && TREE_TYPE (init) == NULL_TREE + && TREE_CODE (type) == ARRAY_TYPE + && !DECL_DECOMPOSITION_P (decl) + && (cxx_dialect >= cxx2a)) + { + /* [dcl.init.string] "An array of ordinary character type [...] + can be initialized by an ordinary string literal [...] by an + appropriately-typed string literal enclosed in braces" only + talks about braces, but GCC has always accepted + + char a[]("foobar"); + + so we continue to do so. */ + tree val = TREE_VALUE (init); + if (TREE_CHAIN (init) == NULL_TREE + && char_type_p (TYPE_MAIN_VARIANT (TREE_TYPE (type))) + && TREE_CODE (tree_strip_any_location_wrapper (val)) + == STRING_CST) + /* If the list has a single element and it's a string literal, + then it's the initializer for the array as a whole. */ + init = val; + else + { + init = build_constructor_from_list (init_list_type_node, init); + CONSTRUCTOR_IS_DIRECT_INIT (init) = true; + CONSTRUCTOR_IS_PAREN_INIT (init) = true; + } + } else if (TREE_CODE (init) == TREE_LIST && TREE_TYPE (init) != unknown_type_node && !MAYBE_CLASS_TYPE_P (type)) @@ -6683,6 +6740,9 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups) init_code = TREE_OPERAND (init_code, 0); if (TREE_CODE (init_code) == INIT_EXPR) { + /* In C++20, the call to build_aggr_init could have created + an INIT_EXPR with a CONSTRUCTOR as the RHS to handle + A(1, 2). */ init = TREE_OPERAND (init_code, 1); init_code = NULL_TREE; /* Don't call digest_init; it's unnecessary and will complain @@ -6736,7 +6796,7 @@ check_initializer (tree decl, tree init, int flags, vec<tree, va_gc> **cleanups) 0, "array %qD initialized by parenthesized " "string literal %qE", decl, DECL_INITIAL (decl)); - init = NULL; + init = NULL_TREE; } } else diff --git a/gcc/cp/tree.c b/gcc/cp/tree.c index 8817860..8b625e8 100644 --- a/gcc/cp/tree.c +++ b/gcc/cp/tree.c @@ -669,6 +669,15 @@ build_aggr_init_expr (tree type, tree init) tree build_cplus_new (tree type, tree init, tsubst_flags_t complain) { + /* This function should cope with what build_special_member_call + can produce. When performing parenthesized aggregate initialization, + it can produce a { }. */ + if (BRACE_ENCLOSED_INITIALIZER_P (init)) + { + gcc_assert (cxx_dialect >= cxx2a); + return finish_compound_literal (type, init, complain); + } + tree rval = build_aggr_init_expr (type, init); tree slot; diff --git a/gcc/cp/typeck2.c b/gcc/cp/typeck2.c index ae00de2..7fda626 100644 --- a/gcc/cp/typeck2.c +++ b/gcc/cp/typeck2.c @@ -1117,6 +1117,10 @@ digest_init_r (tree type, tree init, int nested, int flags, tree stripped_init = init; + if (BRACE_ENCLOSED_INITIALIZER_P (init) + && CONSTRUCTOR_IS_PAREN_INIT (init)) + flags |= LOOKUP_AGGREGATE_PAREN_INIT; + /* Strip NON_LVALUE_EXPRs since we aren't using as an lvalue (g++.old-deja/g++.law/casts2.C). */ if (TREE_CODE (init) == NON_LVALUE_EXPR) @@ -1224,7 +1228,9 @@ digest_init_r (tree type, tree init, int nested, int flags, if ((code != COMPLEX_TYPE || BRACE_ENCLOSED_INITIALIZER_P (stripped_init)) && (SCALAR_TYPE_P (type) || code == REFERENCE_TYPE)) { - if (nested) + /* Narrowing is OK when initializing an aggregate from + a parenthesized list. */ + if (nested && !(flags & LOOKUP_AGGREGATE_PAREN_INIT)) flags |= LOOKUP_NO_NARROWING; init = convert_for_initialization (0, type, init, flags, ICR_INIT, NULL_TREE, 0, @@ -1386,9 +1392,12 @@ static tree massage_init_elt (tree type, tree init, int nested, int flags, tsubst_flags_t complain) { - flags &= LOOKUP_ALLOW_FLEXARRAY_INIT; - flags |= LOOKUP_IMPLICIT; - init = digest_init_r (type, init, nested ? 2 : 1, flags, complain); + int new_flags = LOOKUP_IMPLICIT; + if (flags & LOOKUP_ALLOW_FLEXARRAY_INIT) + new_flags |= LOOKUP_ALLOW_FLEXARRAY_INIT; + if (flags & LOOKUP_AGGREGATE_PAREN_INIT) + new_flags |= LOOKUP_AGGREGATE_PAREN_INIT; + init = digest_init_r (type, init, nested ? 2 : 1, new_flags, complain); /* Strip a simple TARGET_EXPR when we know this is an initializer. */ if (SIMPLE_TARGET_EXPR_P (init)) init = TARGET_EXPR_INITIAL (init); |