aboutsummaryrefslogtreecommitdiff
path: root/gcc/cp
diff options
context:
space:
mode:
authorMarek Polacek <polacek@redhat.com>2019-12-03 15:59:40 +0000
committerMarek Polacek <mpolacek@gcc.gnu.org>2019-12-03 15:59:40 +0000
commit43aae289866f5ea55d187444520412554aa2e171 (patch)
treea3a517242014a3db820710f763749398c1d1e0f0 /gcc/cp
parent577f4a0e5e7f7ef9b5729a3eed79e523cba9dfa9 (diff)
downloadgcc-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/ChangeLog20
-rw-r--r--gcc/cp/call.c51
-rw-r--r--gcc/cp/cp-tree.h7
-rw-r--r--gcc/cp/decl.c68
-rw-r--r--gcc/cp/tree.c9
-rw-r--r--gcc/cp/typeck2.c17
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);