diff options
author | Marek Polacek <polacek@redhat.com> | 2020-04-08 17:03:53 -0400 |
---|---|---|
committer | Marek Polacek <polacek@redhat.com> | 2020-04-09 08:28:46 -0400 |
commit | 830c572428758f134bd001e699a08e622e2452c3 (patch) | |
tree | eb650334e618b8cfd52b357a2f8e84b87eda04fd /gcc/cp | |
parent | 148289004696940ea5828d19e63a1e3791a2fb70 (diff) | |
download | gcc-830c572428758f134bd001e699a08e622e2452c3.zip gcc-830c572428758f134bd001e699a08e622e2452c3.tar.gz gcc-830c572428758f134bd001e699a08e622e2452c3.tar.bz2 |
c++: Fix wrong paren-init of aggregates interference [PR93790]
This PR points out that we are rejecting valid code in C++20. The
problem is that we were surreptitiously transforming
T& t(e)
into
T& t{e}
which is wrong, because the type of e had a conversion function to T,
while aggregate initialization of t from e doesn't work. Therefore, I
was violating a design principle of P0960, which says that any existing
meaning of A(b) should not change. So I think we should only attempt to
aggregate-initialize if the original expression was ill-formed.
Another design principle is that () should work where {} works, so this:
struct S { int i; };
const S& s(1);
has to keep working. Thus the special handling for paren-lists with one
element. (A paren-list with more than one element would give you "error:
expression list treated as compound expression in initializer" C++17.)
PR c++/93790
* call.c (initialize_reference): If the reference binding failed, maybe
try initializing from { }.
* decl.c (grok_reference_init): For T& t(e), set
LOOKUP_AGGREGATE_PAREN_INIT but don't build up a constructor yet.
* g++.dg/cpp2a/paren-init23.C: New test.
* g++.dg/init/aggr14.C: New test.
Diffstat (limited to 'gcc/cp')
-rw-r--r-- | gcc/cp/ChangeLog | 8 | ||||
-rw-r--r-- | gcc/cp/call.c | 14 | ||||
-rw-r--r-- | gcc/cp/decl.c | 19 |
3 files changed, 38 insertions, 3 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index e63f30b..cdd9b52 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,11 @@ +2020-04-09 Marek Polacek <polacek@redhat.com> + + PR c++/93790 + * call.c (initialize_reference): If the reference binding failed, maybe + try initializing from { }. + * decl.c (grok_reference_init): For T& t(e), set + LOOKUP_AGGREGATE_PAREN_INIT but don't build up a constructor yet. + 2020-04-08 Iain Sandoe <iain@sandoe.co.uk> Jun Ma <JunMa@linux.alibaba.com> diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 02220ff..1f3d9d2 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -12196,6 +12196,20 @@ initialize_reference (tree type, tree expr, conv = reference_binding (type, TREE_TYPE (expr), expr, /*c_cast_p=*/false, flags, complain); + /* If this conversion failed, we're in C++20, and we have something like + A& a(b) where A is an aggregate, try again, this time as A& a{b}. */ + if ((!conv || conv->bad_p) + && (flags & LOOKUP_AGGREGATE_PAREN_INIT)) + { + tree e = build_constructor_single (init_list_type_node, NULL_TREE, expr); + CONSTRUCTOR_IS_DIRECT_INIT (e) = true; + CONSTRUCTOR_IS_PAREN_INIT (e) = true; + conversion *c = reference_binding (type, TREE_TYPE (e), e, + /*c_cast_p=*/false, flags, complain); + /* If this worked, use it. */ + if (c && !c->bad_p) + expr = e, conv = c; + } if (!conv || conv->bad_p) { if (complain & tf_error) diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c index a6a1340..1447b89 100644 --- a/gcc/cp/decl.c +++ b/gcc/cp/decl.c @@ -5568,9 +5568,22 @@ grok_reference_init (tree decl, tree type, tree init, int flags) && !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; + /* We don't know yet if we should treat const A& r(1) as + const A& r{1}. */ + if (list_length (init) == 1) + { + flags |= LOOKUP_AGGREGATE_PAREN_INIT; + init = build_x_compound_expr_from_list (init, ELK_INIT, + tf_warning_or_error); + } + /* If the list had more than one element, the code is ill-formed + pre-C++20, so we can build a constructor right away. */ + else + { + 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, |