On 4/8/20 5:52 PM, Marek Polacek wrote:
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.)
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
OK.
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.
---
gcc/cp/call.c | 14 ++++++++++++++
gcc/cp/decl.c | 19 ++++++++++++++++---
gcc/testsuite/g++.dg/cpp2a/paren-init23.C | 19 +++++++++++++++++++
gcc/testsuite/g++.dg/init/aggr14.C | 14 ++++++++++++++
4 files changed, 63 insertions(+), 3 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp2a/paren-init23.C
create mode 100644 gcc/testsuite/g++.dg/init/aggr14.C
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 02220ffb3a1..1f3d9d23b5b 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 a6a1340e631..1447b89e692 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,
diff --git a/gcc/testsuite/g++.dg/cpp2a/paren-init23.C
b/gcc/testsuite/g++.dg/cpp2a/paren-init23.C
new file mode 100644
index 00000000000..6038f63be5a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/paren-init23.C
@@ -0,0 +1,19 @@
+// PR c++/93790 - wrong paren-init of aggregates interference.
+// { dg-do compile { target c++2a } }
+
+struct S {
+ int i;
+};
+const S& s(1);
+
+struct A {
+ int i;
+ A(int);
+};
+const A& a(1);
+
+struct B {
+ int i;
+ B(int) = delete;
+};
+const B& b(1); // { dg-error "use of deleted function" }
diff --git a/gcc/testsuite/g++.dg/init/aggr14.C
b/gcc/testsuite/g++.dg/init/aggr14.C
new file mode 100644
index 00000000000..538b467d467
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/aggr14.C
@@ -0,0 +1,14 @@
+// PR c++/93790 - wrong paren-init of aggregates interference.
+// { dg-do compile }
+
+struct S {};
+class S_refwrap {
+ S& Sref_;
+public:
+ S_refwrap(S& Sref) : Sref_(Sref) {}
+ operator S&() { return Sref_; }
+};
+
+S s;
+S_refwrap r(s);
+S& s2(r);
base-commit: 48242b2c3ac96c0bd5265e77013583ac9ceee97d