diff options
author | Patrick Palka <ppalka@redhat.com> | 2020-04-27 17:06:35 -0400 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2020-04-27 17:13:33 -0400 |
commit | 64da1b761db248f4f0d2235a6055c025fbbc94eb (patch) | |
tree | cd386cb1de5cf78623cf6ce55493058e4c22b602 /gcc | |
parent | 067ebf841300127d9616ddde0b37db03cff60347 (diff) | |
download | gcc-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.
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/ChangeLog | 8 | ||||
-rw-r--r-- | gcc/cp/constexpr.c | 28 | ||||
-rw-r--r-- | gcc/testsuite/ChangeLog | 7 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const23.C | 21 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const24.C | 26 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/cpp1y/constexpr-tracking-const25.C | 66 |
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 ); +} |