aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPatrick Palka <ppalka@redhat.com>2020-04-27 17:06:35 -0400
committerPatrick Palka <ppalka@redhat.com>2020-04-27 17:13:33 -0400
commit64da1b761db248f4f0d2235a6055c025fbbc94eb (patch)
treecd386cb1de5cf78623cf6ce55493058e4c22b602
parent067ebf841300127d9616ddde0b37db03cff60347 (diff)
downloadgcc-64da1b761db248f4f0d2235a6055c025fbbc94eb.zip
gcc-64da1b761db248f4f0d2235a6055c025fbbc94eb.tar.gz
gcc-64da1b761db248f4f0d2235a6055c025fbbc94eb.tar.bz2
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.
-rw-r--r--gcc/cp/ChangeLog8
-rw-r--r--gcc/cp/constexpr.c28
-rw-r--r--gcc/testsuite/ChangeLog7
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const23.C21
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const24.C26
-rw-r--r--gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const25.C66
6 files changed, 155 insertions, 1 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index d0fd009..8cb958f 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 6b3e514..637cb74 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 8b23374..39a984c 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 0000000..c6643c7
--- /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 0000000..2c923f6
--- /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 0000000..499dd5c
--- /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 );
+}