[gcc(refs/vendors/ARM/heads/arm-struct-reorg-wip)] c++: Delegating constructor in constexpr init [PR94772]

Tamar Christina tnfchris@gcc.gnu.org
Fri Jul 17 14:48:16 GMT 2020


https://gcc.gnu.org/g:64da1b761db248f4f0d2235a6055c025fbbc94eb

commit 64da1b761db248f4f0d2235a6055c025fbbc94eb
Author: Patrick Palka <ppalka@redhat.com>
Date:   Mon Apr 27 17:06:35 2020 -0400

    c++: Delegating constructor in constexpr init [PR94772]
    
    In the first testcase below, the call to the target constructor foo{} from foo's
    delegating constructor is encoded as the INIT_EXPR
    
      *(struct foo *) this = AGGR_INIT_EXPR <4, __ct_comp, D.2140, ...>;
    
    During initialization of the variable 'bar', we prematurely set TREE_READONLY on
    bar's CONSTRUCTOR in two places before the outer delegating constructor has
    returned: first, at the end of cxx_eval_call_expression after evaluating the RHS
    of the above INIT_EXPR, and second, at the end of cxx_eval_store_expression
    after having finished evaluating the above INIT_EXPR.  This then prevents the
    rest of the outer delegating constructor from mutating 'bar'.
    
    This (hopefully minimally risky) patch makes cxx_eval_call_expression refrain
    from setting TREE_READONLY when evaluating the target constructor of a
    delegating constructor.  It also makes cxx_eval_store_expression refrain from
    setting TREE_READONLY when the object being initialized is "*this', on the basis
    that it should be the responsibility of the routine that set 'this' in the first
    place to set the object's TREE_READONLY appropriately.
    
    gcc/cp/ChangeLog:
    
            PR c++/94772
            * constexpr.c (cxx_eval_call_expression): Don't set new_obj if we're
            evaluating the target constructor of a delegating constructor.
            (cxx_eval_store_expression): Don't set TREE_READONLY if the LHS of the
            INIT_EXPR is '*this'.
    
    gcc/testsuite/ChangeLog:
    
            PR c++/94772
            * g++.dg/cpp1y/constexpr-tracking-const23.C: New test.
            * g++.dg/cpp1y/constexpr-tracking-const24.C: New test.
            * g++.dg/cpp1y/constexpr-tracking-const25.C: New test.

Diff:
---
 gcc/cp/ChangeLog                                   |  8 +++
 gcc/cp/constexpr.c                                 | 28 ++++++++-
 gcc/testsuite/ChangeLog                            |  7 +++
 .../g++.dg/cpp1y/constexpr-tracking-const23.C      | 21 +++++++
 .../g++.dg/cpp1y/constexpr-tracking-const24.C      | 26 +++++++++
 .../g++.dg/cpp1y/constexpr-tracking-const25.C      | 66 ++++++++++++++++++++++
 6 files changed, 155 insertions(+), 1 deletion(-)

diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index d0fd0096c58..8cb958f36fb 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -1,3 +1,11 @@
+2020-04-27  Patrick Palka  <ppalka@redhat.com>
+
+	PR c++/94772
+	* constexpr.c (cxx_eval_call_expression): Don't set new_obj if we're
+	evaluating the target constructor of a delegating constructor.
+	(cxx_eval_store_expression): Don't set TREE_READONLY if the LHS of the
+	INIT_EXPR is '*this'.
+
 2020-04-26  Marek Polacek  <polacek@redhat.com>
 
 	PR c++/90320
diff --git a/gcc/cp/constexpr.c b/gcc/cp/constexpr.c
index 6b3e514398b..637cb746576 100644
--- a/gcc/cp/constexpr.c
+++ b/gcc/cp/constexpr.c
@@ -2371,6 +2371,21 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t,
       STRIP_NOPS (new_obj);
       if (TREE_CODE (new_obj) == ADDR_EXPR)
 	new_obj = TREE_OPERAND (new_obj, 0);
+
+      if (ctx->call && ctx->call->fundef
+	  && DECL_CONSTRUCTOR_P (ctx->call->fundef->decl))
+	{
+	  tree cur_obj = TREE_VEC_ELT (ctx->call->bindings, 0);
+	  STRIP_NOPS (cur_obj);
+	  if (TREE_CODE (cur_obj) == ADDR_EXPR)
+	    cur_obj = TREE_OPERAND (cur_obj, 0);
+	  if (new_obj == cur_obj)
+	    /* We're calling the target constructor of a delegating
+	       constructor, or accessing a base subobject through a
+	       NOP_EXPR as part of a call to a base constructor, so
+	       there is no new (sub)object.  */
+	    new_obj = NULL_TREE;
+	}
     }
 
   tree result = NULL_TREE;
@@ -4950,7 +4965,18 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, tree t,
   if (TREE_CODE (t) == INIT_EXPR
       && TREE_CODE (*valp) == CONSTRUCTOR
       && TYPE_READONLY (type))
-    TREE_READONLY (*valp) = true;
+    {
+      if (INDIRECT_REF_P (target)
+	  && (is_this_parameter
+	      (tree_strip_nop_conversions (TREE_OPERAND (target, 0)))))
+	/* We've just initialized '*this' (perhaps via the target
+	   constructor of a delegating constructor).  Leave it up to the
+	   caller that set 'this' to set TREE_READONLY appropriately.  */
+	gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p
+			     (TREE_TYPE (target), type));
+      else
+	TREE_READONLY (*valp) = true;
+    }
 
   /* Update TREE_CONSTANT and TREE_SIDE_EFFECTS on enclosing
      CONSTRUCTORs, if any.  */
diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog
index 8b23374c591..39a984c05e1 100644
--- a/gcc/testsuite/ChangeLog
+++ b/gcc/testsuite/ChangeLog
@@ -1,3 +1,10 @@
+2020-04-27  Patrick Palka  <ppalka@redhat.com>
+
+	PR c++/94772
+	* g++.dg/cpp1y/constexpr-tracking-const23.C: New test.
+	* g++.dg/cpp1y/constexpr-tracking-const24.C: New test.
+	* g++.dg/cpp1y/constexpr-tracking-const25.C: New test.
+
 2020-04-27  Szabolcs Nagy  <szabolcs.nagy@arm.com>
 
 	PR target/94697
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const23.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const23.C
new file mode 100644
index 00000000000..c6643c78a6f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const23.C
@@ -0,0 +1,21 @@
+// PR c++/94772
+// { dg-do compile { target c++14 } }
+
+struct foo
+{
+  int x{};
+
+  constexpr foo() noexcept = default;
+
+  constexpr foo(int a) : foo{}
+  { x = -a; }
+
+  constexpr foo(int a, int b) : foo{a}
+  { x += a + b; }
+};
+
+int main()
+{
+  constexpr foo bar{1, 2};
+  static_assert(bar.x == 2, "");
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const24.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const24.C
new file mode 100644
index 00000000000..2c923f69cf4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const24.C
@@ -0,0 +1,26 @@
+// PR c++/94772
+// { dg-do compile { target c++14 } }
+
+struct base
+{
+  base() = default;
+
+  constexpr base(int) : base{} { }
+};
+
+struct foo : base
+{
+  int x{};
+
+  constexpr foo(int a) : base{a}
+  { x = -a; }
+
+  constexpr foo(int a, int b) : foo{a}
+  { x += a + b; }
+};
+
+int main()
+{
+  constexpr foo bar{1, 2};
+  static_assert(bar.x == 2, "");
+}
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const25.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const25.C
new file mode 100644
index 00000000000..499dd5c6937
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const25.C
@@ -0,0 +1,66 @@
+// PR c++/94772
+// { dg-do compile { target c++14 } }
+
+template<int>
+struct base
+{
+  int y{};
+
+  base() = default;
+
+  constexpr base(int a) : base{}
+  { y = a; }
+};
+
+struct foo : base<1>, base<2>
+{
+  int x{};
+
+  constexpr foo() : base<2>{}
+  {
+    ++x; --x;
+    ++base<1>::y;
+    ++base<2>::y;
+  }
+
+  constexpr foo(int a) : base<2>{a}
+  {
+    x = -base<2>::y;
+    ++base<1>::y;
+    ++base<2>::y;
+  }
+
+  constexpr foo(int a, int b) : foo{a}
+  {
+    x += a + b;
+    ++base<1>::y;
+    ++base<2>::y;
+  }
+
+  constexpr foo(int a, int b, int c) : base<1>{a}
+  {
+    x += a + b + c;
+    ++base<1>::y;
+    ++base<2>::y;
+  }
+};
+
+#define SA(X) static_assert(X, #X)
+
+int main()
+{
+  constexpr foo bar1{1, 2};
+  SA( bar1.x == 2 );
+  SA( bar1.base<1>::y == 2 );
+  SA( bar1.base<2>::y == 3 );
+
+  constexpr foo bar2{1, 2, 3};
+  SA( bar2.x == 6 );
+  SA( bar2.base<1>::y == 2 );
+  SA( bar2.base<2>::y == 1 );
+
+  constexpr foo bar3{};
+  SA( bar3.x == 0 );
+  SA( bar3.base<1>::y == 1 );
+  SA( bar3.base<2>::y == 1 );
+}


More information about the Gcc-cvs mailing list