On Fri, 28 Apr 2023, Patrick Palka wrote: > After mechanically replacing RESULT_DECL within a constexpr call result > (for sake of RVO), we can in some cases simplify the call result > further. > > In the below testcase the result of get() during evaluation of a's > initializer is the self-referential CONSTRUCTOR: > > {._M_p=(char *) &<retval>._M_local_buf} > > which after replacing RESULT_DECL with ctx->object (aka *D.2603, where > the D.2603 temporary points to the current element of _M_elems under > construction) becomes: > > {._M_p=(char *) &D.2603->_M_local_buf} > > but what we really want is: > > {._M_p=(char *) &a._M_elems[0]._M_local_buf}. > > so that the value of _M_p is independent of the value of the mutable > D.2603 temporary. > > So to that end, it seems we should constexpr evaluate the result again > after RESULT_DECL replacement, which is what this patch implements. > > Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for > trunk? > > PR libstdc++/105440 > > gcc/cp/ChangeLog: > > * constexpr.cc (cxx_eval_call_expression): If any RESULT_DECLs get > replaced in the call result, try further evaluating the result. > > gcc/testsuite/ChangeLog: > > * g++.dg/cpp2a/constexpr-dtor16.C: New test. > --- > gcc/cp/constexpr.cc | 12 +++++- > gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C | 39 +++++++++++++++++++ > 2 files changed, 49 insertions(+), 2 deletions(-) > create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C > > diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc > index d1097764b10..22a1609e664 100644 > --- a/gcc/cp/constexpr.cc > +++ b/gcc/cp/constexpr.cc > @@ -3213,7 +3213,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, > tree t, > && CLASS_TYPE_P (TREE_TYPE (res)) > && !is_empty_class (TREE_TYPE (res))) > if (replace_decl (&result, res, ctx->object)) > - cacheable = false; > + { > + cacheable = false; > + result = cxx_eval_constant_expression (ctx, result, lval, > + non_constant_p, > + overflow_p); > + } > } > else > /* Couldn't get a function copy to evaluate. */ > @@ -5988,9 +5993,12 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, > tree t, > object = probe; > else > { > + tree orig_probe = probe; > probe = cxx_eval_constant_expression (ctx, probe, vc_glvalue, > non_constant_p, overflow_p); > evaluated = true; > + if (orig_probe == target) > + target = probe;
Whoops, thanks to an accidental git commit --amend this patch contains an alternative approach that I considered: in cxx_eval_store_expression, ensure that we always set ctx->object to a fully reduced result (so &a._M_elems[0] instead of of *D.2603 in this case), which means later RESULT_DECL replacement with ctx->object should yield an already reduced result as well. But with this approach I ran into a bogus "modifying const object" error on cpp1y/constexpr-tracking-const23.C so I gave up on it :( Here's the correct patch: PR libstdc++/105440 gcc/cp/ChangeLog: * constexpr.cc (cxx_eval_call_expression): If any RESULT_DECLs get replaced in the call result, try further evaluating the result. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/constexpr-dtor16.C: New test. --- gcc/cp/constexpr.cc | 7 +++- gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C | 39 +++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc index d1097764b10..9dbbf6eec03 100644 --- a/gcc/cp/constexpr.cc +++ b/gcc/cp/constexpr.cc @@ -3213,7 +3213,12 @@ cxx_eval_call_expression (const constexpr_ctx *ctx, tree t, && CLASS_TYPE_P (TREE_TYPE (res)) && !is_empty_class (TREE_TYPE (res))) if (replace_decl (&result, res, ctx->object)) - cacheable = false; + { + cacheable = false; + result = cxx_eval_constant_expression (ctx, result, lval, + non_constant_p, + overflow_p); + } } else /* Couldn't get a function copy to evaluate. */ diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C new file mode 100644 index 00000000000..707a3e025b1 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C @@ -0,0 +1,39 @@ +// PR c++/105440 +// { dg-do compile { target c++20 } } + +struct basic_string { + char _M_local_buf[32]; + char* _M_p; + constexpr basic_string() : _M_p{_M_local_buf} { } + constexpr void f() { if (_M_p) { } } + constexpr ~basic_string() { if (_M_p) { } } +}; + +template<int N> +struct array { + basic_string _M_elems[N]; +}; + +constexpr basic_string get() { return {}; } + +constexpr bool f1() { + array<1> a{get()}; + a._M_elems[0].f(); + + return true; +} + +constexpr bool f2() { + array<2> a2{get(), get()}; + array<3> a3{get(), get(), get()}; + + for (basic_string& e : a2._M_elems) + e.f(); + for (basic_string& e : a3._M_elems) + e.f(); + + return true; +} + +static_assert(f1()); +static_assert(f2()); -- 2.40.1.445.gf85cd430b1 > if (*non_constant_p) > return t; > } > @@ -6154,7 +6162,7 @@ cxx_eval_store_expression (const constexpr_ctx *ctx, > tree t, > if (!empty_base && !(same_type_ignoring_top_level_qualifiers_p > (initialized_type (init), type))) > { > - gcc_assert (is_empty_class (TREE_TYPE (target))); > + gcc_assert (is_empty_class (TREE_TYPE (TREE_OPERAND (t, 0)))); > empty_base = true; > } > > diff --git a/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C > b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C > new file mode 100644 > index 00000000000..707a3e025b1 > --- /dev/null > +++ b/gcc/testsuite/g++.dg/cpp2a/constexpr-dtor16.C > @@ -0,0 +1,39 @@ > +// PR c++/105440 > +// { dg-do compile { target c++20 } } > + > +struct basic_string { > + char _M_local_buf[32]; > + char* _M_p; > + constexpr basic_string() : _M_p{_M_local_buf} { } > + constexpr void f() { if (_M_p) { } } > + constexpr ~basic_string() { if (_M_p) { } } > +}; > + > +template<int N> > +struct array { > + basic_string _M_elems[N]; > +}; > + > +constexpr basic_string get() { return {}; } > + > +constexpr bool f1() { > + array<1> a{get()}; > + a._M_elems[0].f(); > + > + return true; > +} > + > +constexpr bool f2() { > + array<2> a2{get(), get()}; > + array<3> a3{get(), get(), get()}; > + > + for (basic_string& e : a2._M_elems) > + e.f(); > + for (basic_string& e : a3._M_elems) > + e.f(); > + > + return true; > +} > + > +static_assert(f1()); > +static_assert(f2()); > -- > 2.40.1.445.gf85cd430b1 > >