Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk/14?

-- >8 --
build_over_call has:

          t = build2 (MODIFY_EXPR, void_type_node,
                      build2 (MEM_REF, array_type, arg0, alias_set),
                      build2 (MEM_REF, array_type, arg, alias_set));
          val = build2 (COMPOUND_EXPR, TREE_TYPE (to), t, to);

which creates an expression that can look like:

  d = MEM <unsigned char[4]> [(struct A *)&TARGET_EXPR <D.2894, foo()]
    = MEM <unsigned char[4]> [(struct A *)(const struct A &) &e],
      TARGET_EXPR <D.2894, foo()>

that is, a COMPOUND_EXPR where a TARGET_EXPR is used twice, and its
address is taken in the left-hand side operand, so it can't be elided.
But set_target_expr_eliding simply recurses on the second operand of
a COMPOUND_EXPR and marks the TARGET_EXPR as eliding.  This then causes
a crash.

We cannot break_out_target_exprs here, that would cause a wrong-code
problem which we don't have a test for, so I'm adding align5.C to
exercise this.  So it seems clear that the fix will be along the lines
of clearing the TARGET_EXPR_ELIDING_P flag.  A simple fix would be:

@@ -1703,6 +1703,9 @@ cp_genericize_r (tree *stmt_p, int *walk_subtrees, void 
*data)
   switch (TREE_CODE (stmt))
     {
     case ADDR_EXPR:
+      if (TREE_CODE (TREE_OPERAND (stmt, 0)) == TARGET_EXPR
+         && TARGET_EXPR_ELIDING_P (TREE_OPERAND (stmt, 0)))
+       TARGET_EXPR_ELIDING_P (TREE_OPERAND (stmt, 0)) = false;
       if (is_invisiref_parm (TREE_OPERAND (stmt, 0)))
        {
          /* If in an OpenMP context, note var uses.  */

but it seems preferable to not set the flag in the first place rather
than clearing it later.

        PR c++/117512

gcc/cp/ChangeLog:

        * cp-tree.h (set_target_expr_eliding): Adjust.
        * typeck2.cc (expr_has_addr_taken_r): New.
        (expr_has_addr_taken): New.
        (set_target_expr_eliding): Set TARGET_EXPR_ELIDING_P only when
        !expr_has_addr_taken.

gcc/testsuite/ChangeLog:

        * g++.dg/cpp0x/alignas23.C: New test.
        * g++.dg/ext/align3.C: New test.
        * g++.dg/ext/align4.C: New test.
        * g++.dg/ext/align5.C: New test.
---
 gcc/cp/cp-tree.h                       |  2 +-
 gcc/cp/typeck2.cc                      | 46 ++++++++++++++++++++++----
 gcc/testsuite/g++.dg/cpp0x/alignas23.C | 15 +++++++++
 gcc/testsuite/g++.dg/ext/align3.C      | 14 ++++++++
 gcc/testsuite/g++.dg/ext/align4.C      | 14 ++++++++
 gcc/testsuite/g++.dg/ext/align5.C      | 18 ++++++++++
 6 files changed, 102 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/alignas23.C
 create mode 100644 gcc/testsuite/g++.dg/ext/align3.C
 create mode 100644 gcc/testsuite/g++.dg/ext/align4.C
 create mode 100644 gcc/testsuite/g++.dg/ext/align5.C

diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index a839ad6d28a..0d3094d1c8b 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -8594,7 +8594,7 @@ extern tree build_functional_cast         (location_t, 
tree, tree,
                                                 tsubst_flags_t);
 extern tree add_exception_specifier            (tree, tree, tsubst_flags_t);
 extern tree merge_exception_specifiers         (tree, tree);
-extern void set_target_expr_eliding            (tree);
+extern void set_target_expr_eliding            (tree, tree = NULL_TREE);
 extern tree cp_build_init_expr                 (location_t, tree, tree);
 inline tree cp_build_init_expr (tree t, tree i)
 { return cp_build_init_expr (input_location, t, i); }
diff --git a/gcc/cp/typeck2.cc b/gcc/cp/typeck2.cc
index 1adc05aa86d..457b36bf0ff 100644
--- a/gcc/cp/typeck2.cc
+++ b/gcc/cp/typeck2.cc
@@ -2785,25 +2785,59 @@ require_complete_eh_spec_types (tree fntype, tree decl)
     }
 }
 
+/* walk_tree callback to figure out if an expression has its address taken.  */
+
+static tree
+expr_has_addr_taken_r (tree *tp, int *walk_subtrees, void *data)
+{
+  tree full_expr = *static_cast<tree *>(data);
+  tree t = *tp;
+
+  if (full_expr == t)
+    {
+      *walk_subtrees = false;
+      return NULL_TREE;
+    }
+
+  if (TREE_CODE (t) == ADDR_EXPR && TREE_OPERAND (t, 0) == full_expr)
+    return t;
+
+  return NULL_TREE;
+}
+
+/* Return true iff EXPR has its address taken in FULL_EXPR.  */
+
+static bool
+expr_has_addr_taken (tree full_expr, tree expr)
+{
+  return !!cp_walk_tree_without_duplicates (&full_expr,
+                                           expr_has_addr_taken_r,
+                                           &expr);
+}
+
 /* Record that any TARGET_EXPR in T are going to be elided in
-   cp_gimplify_init_expr (or sooner).  */
+   cp_gimplify_init_expr (or sooner).  WHOLE is the full expression.  */
 
 void
-set_target_expr_eliding (tree t)
+set_target_expr_eliding (tree t, tree whole/*=NULL_TREE*/)
 {
   if (!t)
     return;
   switch (TREE_CODE (t))
     {
     case TARGET_EXPR:
-      TARGET_EXPR_ELIDING_P (t) = true;
+      /* build_over_call can give us a COMPOUND_EXPR that looks like:
+         ...&TARGET_EXPR<D.1234>..., TARGET_EXPR<D.1234>
+        in which case we can't mark the TARGET_EXPR as eliding.  */
+      if (!(whole && expr_has_addr_taken (whole, t)))
+       TARGET_EXPR_ELIDING_P (t) = true;
       break;
     case COMPOUND_EXPR:
-      set_target_expr_eliding (TREE_OPERAND (t, 1));
+      set_target_expr_eliding (TREE_OPERAND (t, 1), whole ? whole : t);
       break;
     case COND_EXPR:
-      set_target_expr_eliding (TREE_OPERAND (t, 1));
-      set_target_expr_eliding (TREE_OPERAND (t, 2));
+      set_target_expr_eliding (TREE_OPERAND (t, 1), whole ? whole : t);
+      set_target_expr_eliding (TREE_OPERAND (t, 2), whole ? whole : t);
       break;
 
     default:
diff --git a/gcc/testsuite/g++.dg/cpp0x/alignas23.C 
b/gcc/testsuite/g++.dg/cpp0x/alignas23.C
new file mode 100644
index 00000000000..3c218a3542c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/alignas23.C
@@ -0,0 +1,15 @@
+// PR c++/117512
+// { dg-do compile { target c++11 } }
+
+struct A {
+  alignas(sizeof (long long)) int b;
+  ~A ();
+};
+A foo (int);
+
+void
+bar ()
+{
+  A e = { 0 };
+  A d = foo (0) = e;
+}
diff --git a/gcc/testsuite/g++.dg/ext/align3.C 
b/gcc/testsuite/g++.dg/ext/align3.C
new file mode 100644
index 00000000000..6a20dfc57b1
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/align3.C
@@ -0,0 +1,14 @@
+// PR c++/117512
+
+struct A {
+  __attribute__((aligned)) int b;
+  ~A ();
+};
+A foo (int);
+
+void
+bar ()
+{
+  A e = { 0 };
+  A d = foo (0) = e;
+}
diff --git a/gcc/testsuite/g++.dg/ext/align4.C 
b/gcc/testsuite/g++.dg/ext/align4.C
new file mode 100644
index 00000000000..b0d83e30237
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/align4.C
@@ -0,0 +1,14 @@
+// PR c++/117512
+
+struct __attribute__((aligned (2 * sizeof (int)))) A {
+  int b;
+  ~A ();
+};
+A foo (int);
+
+void
+bar ()
+{
+  A e = { 0 };
+  A d = foo (0) = e;
+}
diff --git a/gcc/testsuite/g++.dg/ext/align5.C 
b/gcc/testsuite/g++.dg/ext/align5.C
new file mode 100644
index 00000000000..7e821274352
--- /dev/null
+++ b/gcc/testsuite/g++.dg/ext/align5.C
@@ -0,0 +1,18 @@
+// PR c++/117512
+// { dg-do run }
+
+struct A {
+  __attribute__((aligned(2 * sizeof (int)))) int i;
+  ~A() {}
+};
+
+A foo () { A a = { 19 }; return a; }
+
+int
+main ()
+{
+  A a = { 42 };
+  A r = foo () = a;
+  if (r.i != 42)
+    __builtin_abort ();
+}

base-commit: 31dcf941ac78c4b1b01dc4b2ce9809f0209153b8
-- 
2.48.1

Reply via email to