c++: Fix wrong paren-init of aggregates interference [PR93790]
authorMarek Polacek <polacek@redhat.com>
Wed, 8 Apr 2020 21:03:53 +0000 (17:03 -0400)
committerMarek Polacek <polacek@redhat.com>
Thu, 9 Apr 2020 12:28:46 +0000 (08:28 -0400)
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.

gcc/cp/ChangeLog
gcc/cp/call.c
gcc/cp/decl.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp2a/paren-init23.C [new file with mode: 0644]
gcc/testsuite/g++.dg/init/aggr14.C [new file with mode: 0644]

index e63f30bf3c1d44dc498178a8fe50a82705f91044..cdd9b52915a1b7e3bf4abcb117ef5964923883ac 100644 (file)
@@ -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>
 
index 02220ffb3a15a04cdb07164357049a5dcaae97b7..1f3d9d23b5b269028f9d43918d3d49c1fb5675cd 100644 (file)
@@ -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)
index a6a1340e63179f1d7719361b49878e2940955987..1447b89e69286851c5b5c127e0745227feee322e 100644 (file)
@@ -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,
index 6a972217610cfa46adebc1ee88c238752ce4acf3..a5bd5614e5adff77c8c0c8bca2cc012cadb867c2 100644 (file)
@@ -1,3 +1,9 @@
+2020-04-09  Marek Polacek  <polacek@redhat.com>
+
+       PR c++/93790
+       * g++.dg/cpp2a/paren-init23.C: New test.
+       * g++.dg/init/aggr14.C: New test.
+
 2020-04-09  Jan Hubicka  <hubicka@ucw.cz>
 
        PR tree-optimization/91322
diff --git a/gcc/testsuite/g++.dg/cpp2a/paren-init23.C b/gcc/testsuite/g++.dg/cpp2a/paren-init23.C
new file mode 100644 (file)
index 0000000..6038f63
--- /dev/null
@@ -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 (file)
index 0000000..538b467
--- /dev/null
@@ -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);