Hi!

The following testcases are rejects-valid or wrong-code, because
when we make copies of the function for constexpr evaluation purposes (the
primary intent is have the functions as is, with no folding whatsoever, so
we diagnose everything), the inliner used under the hood to copy the function
actually folds in some cases.  This folding is done there to canonicalize
some cases (MEM_REFs, INDIRECT_REFs, ADDR_EXRPs), but such canonicalization
is only really needed if we replace their operands by something different
(e.g. replace a PARM_DECL for the corresponding value etc.).  When doing
copy_fn, all we are changing is one set of decls for another set of decls
of the same category.

The following patch avoids those foldings during copy_fn.

Bootstrapped/regtested on x86_64-linux and i686-linux, ok for trunk and
after a while to 9.2?

2019-05-10  Jakub Jelinek  <ja...@redhat.com>

        PR c++/90383
        * tree-inline.h (struct copy_body_data): Add do_not_fold member.
        * tree-inline.c (remap_gimple_op_r): Avoid folding expressions if
        id->do_not_fold.
        (copy_tree_body_r): Likewise.
        (copy_fn): Set id.do_not_fold to true.

        * g++.dg/cpp1y/constexpr-90383-1.C: New test.
        * g++.dg/cpp1y/constexpr-90383-2.C: New test.

--- gcc/tree-inline.h.jj        2019-05-08 19:04:58.947797821 +0200
+++ gcc/tree-inline.h   2019-05-09 10:18:43.567373908 +0200
@@ -113,6 +113,9 @@ struct copy_body_data
   /* True if trees may not be unshared.  */
   bool do_not_unshare;
 
+  /* True if trees should not be folded during the copying.  */
+  bool do_not_fold;
+
   /* True if new declarations may not be created during type remapping.  */
   bool prevent_decl_creation_for_types;
 
--- gcc/tree-inline.c.jj        2019-05-08 19:04:58.949797788 +0200
+++ gcc/tree-inline.c   2019-05-09 10:41:49.691949208 +0200
@@ -1101,7 +1101,7 @@ remap_gimple_op_r (tree *tp, int *walk_s
       /* Otherwise, just copy the node.  Note that copy_tree_r already
         knows not to copy VAR_DECLs, etc., so this is safe.  */
 
-      if (TREE_CODE (*tp) == MEM_REF)
+      if (TREE_CODE (*tp) == MEM_REF && !id->do_not_fold)
        {
          /* We need to re-canonicalize MEM_REFs from inline substitutions
             that can happen when a pointer argument is an ADDR_EXPR.
@@ -1327,11 +1327,11 @@ copy_tree_body_r (tree *tp, int *walk_su
              tree type = TREE_TYPE (*tp);
              tree ptr = id->do_not_unshare ? *n : unshare_expr (*n);
              tree old = *tp;
-             *tp = gimple_fold_indirect_ref (ptr);
+             *tp = id->do_not_fold ? NULL : gimple_fold_indirect_ref (ptr);
              if (! *tp)
                {
                  type = remap_type (type, id);
-                 if (TREE_CODE (ptr) == ADDR_EXPR)
+                 if (TREE_CODE (ptr) == ADDR_EXPR && !id->do_not_fold)
                    {
                      *tp
                        = fold_indirect_ref_1 (EXPR_LOCATION (ptr), type, ptr);
@@ -1360,7 +1360,7 @@ copy_tree_body_r (tree *tp, int *walk_su
              return NULL;
            }
        }
-      else if (TREE_CODE (*tp) == MEM_REF)
+      else if (TREE_CODE (*tp) == MEM_REF && !id->do_not_fold)
        {
          /* We need to re-canonicalize MEM_REFs from inline substitutions
             that can happen when a pointer argument is an ADDR_EXPR.
@@ -1432,7 +1432,8 @@ copy_tree_body_r (tree *tp, int *walk_su
 
          /* Handle the case where we substituted an INDIRECT_REF
             into the operand of the ADDR_EXPR.  */
-         if (TREE_CODE (TREE_OPERAND (*tp, 0)) == INDIRECT_REF)
+         if (TREE_CODE (TREE_OPERAND (*tp, 0)) == INDIRECT_REF
+             && !id->do_not_fold)
            {
              tree t = TREE_OPERAND (TREE_OPERAND (*tp, 0), 0);
              if (TREE_TYPE (t) != TREE_TYPE (*tp))
@@ -6370,6 +6371,7 @@ copy_fn (tree fn, tree& parms, tree& res
      since front-end specific mechanisms may rely on sharing.  */
   id.regimplify = false;
   id.do_not_unshare = true;
+  id.do_not_fold = true;
 
   /* We're not inside any EH region.  */
   id.eh_lp_nr = 0;
--- gcc/testsuite/g++.dg/cpp1y/constexpr-90383-1.C.jj   2019-05-09 
10:49:10.222509867 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-90383-1.C      2019-05-09 
10:48:46.538910236 +0200
@@ -0,0 +1,15 @@
+// PR c++/90383
+// { dg-do compile { target c++14 } }
+
+struct alignas(8) A { constexpr A (bool x) : a(x) {} A () = delete; bool a; };
+struct B { A b; };
+
+constexpr bool
+foo ()
+{
+  B w{A (true)};
+  w.b = A (true);
+  return w.b.a;
+}
+
+static_assert (foo (), "");
--- gcc/testsuite/g++.dg/cpp1y/constexpr-90383-2.C.jj   2019-05-09 
10:49:18.194375099 +0200
+++ gcc/testsuite/g++.dg/cpp1y/constexpr-90383-2.C      2019-05-09 
10:51:08.433511507 +0200
@@ -0,0 +1,22 @@
+// PR c++/90383
+// { dg-do run { target c++14 } }
+// { dg-options "-O2" }
+
+extern "C" void abort ();
+struct alignas(8) A { constexpr A (bool x) : a(x) {} A () = default; bool a; };
+struct B { A b; };
+
+constexpr bool
+foo ()
+{
+  B w{A (true)};
+  w.b = A (true);
+  return w.b.a;
+}
+
+int
+main ()
+{
+  if (!foo ())
+    abort ();
+}

        Jakub

Reply via email to