From: Marek Polacek <polacek@redhat.com>
To: Jason Merrill <jason@redhat.com>
Cc: GCC Patches <gcc-patches@gcc.gnu.org>
Subject: [PATCH v2] c++: fix broken copy elision with nested TARGET_EXPRs [PR105550]
Date: Fri, 24 Jun 2022 20:30:22 -0400 [thread overview]
Message-ID: <YrZXHg4qzuzp3anZ@redhat.com> (raw)
In-Reply-To: <b66def4d-3c06-026d-fb15-63bace165c1b@redhat.com>
On Thu, Jun 02, 2022 at 05:08:54PM -0400, Jason Merrill wrote:
> On 5/26/22 11:01, Marek Polacek wrote:
> > In this problem, we are failing to properly perform copy elision with
> > a conditional operator, so this:
> >
> > constexpr A a = true ? A{} : A{};
> >
> > fails with:
> >
> > error: 'A{((const A*)(&<anonymous>))}' is not a constant expression
> >
> > The whole initializer is
> >
> > TARGET_EXPR <D.2395, 1 ? TARGET_EXPR <D.2393, {.p=(const struct A *) &<PLACEHOLDER_EXPR struct A>}> : TARGET_EXPR <D.2394, {.p=(const struct A *) &<PLACEHOLDER_EXPR struct A>}>>
> >
> > where the outermost TARGET_EXPR is elided, but not the nested ones.
> > Then we end up replacing the PLACEHOLDER_EXPRs with the temporaries the
> > TARGET_EXPRs represent, which is precisely what should *not* happen with
> > copy elision.
> >
> > I've tried the approach of tweaking ctx->object, but I ran into gazillion
> > problems with that. I thought that I would let cxx_eval_constant_expression
> > /TARGET_EXPR create a new object only when ctx->object was null, then
> > adjust setting of ctx->object in places like cxx_bind_parameters_in_call
> > and cxx_eval_component_reference but that failed completely. Sometimes
> > ctx->object has to be reset, sometimes it cannot be reset, 'this' needed
> > special handling, etc. I gave up.
> > > But now that we have potential_prvalue_result_of, a simple but less
> > elegant solution is the following. I thought about setting a flag on
> > a TARGET_EXPR to avoid adding ctx.full_expr, but a new flag would be
> > overkill and using TARGET_EXPR_DIRECT_INIT_P broke things.
Sorry it's taken me so long to get back to this.
> This doesn't seem like a general solution; the same issue would also apply
> to ?: of TARGET_EXPR when it's a subexpression rather than the full
> expression, like f(true ? A{} : B{}).
You're right.
> Another simple approach, but more general, would be to routinely strip
> TARGET_EXPR from the operands of ?: like we do in various other places in
> constexpr.c.
How about this, then?
Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
-- >8 --
In this problem, we are failing to properly perform copy elision with
a conditional operator, so this:
constexpr A a = true ? A{} : A{};
fails with:
error: 'A{((const A*)(&<anonymous>))}' is not a constant expression
The whole initializer is
TARGET_EXPR <D.2395, 1 ? TARGET_EXPR <D.2393, {.p=(const struct A *) &<PLACEHOLDER_EXPR struct A>}> : TARGET_EXPR <D.2394, {.p=(const struct A *) &<PLACEHOLDER_EXPR struct A>}>>
where the outermost TARGET_EXPR is elided, but not the nested ones.
Then we end up replacing the PLACEHOLDER_EXPRs with the temporaries the
TARGET_EXPRs represent, which is precisely what should *not* happen with
copy elision.
I've tried the approach of tweaking ctx->object, but I ran into gazillion
problems with that. I thought that I would let cxx_eval_constant_expression
/TARGET_EXPR create a new object only when ctx->object was null, then
adjust setting of ctx->object in places like cxx_bind_parameters_in_call
and cxx_eval_component_reference but that failed completely. Sometimes
ctx->object has to be reset, sometimes it cannot be reset, 'this' needed
special handling, etc. I gave up.
Instead, this patch strips TARGET_EXPRs from the operands of ?: like
we do in various other places in constexpr.c.
PR c++/105550
gcc/cp/ChangeLog:
* constexpr.cc (cxx_eval_conditional_expression): Strip TARGET_EXPRs.
gcc/testsuite/ChangeLog:
* g++.dg/cpp1y/nsdmi-aggr16.C: Remove FIXME.
* g++.dg/cpp1y/nsdmi-aggr17.C: Remove FIXME.
* g++.dg/cpp0x/constexpr-elision1.C: New test.
* g++.dg/cpp1y/constexpr-elision1.C: New test.
---
gcc/cp/constexpr.cc | 7 +++
.../g++.dg/cpp0x/constexpr-elision1.C | 16 ++++++
.../g++.dg/cpp1y/constexpr-elision1.C | 53 +++++++++++++++++++
gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C | 5 +-
gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C | 5 +-
5 files changed, 80 insertions(+), 6 deletions(-)
create mode 100644 gcc/testsuite/g++.dg/cpp0x/constexpr-elision1.C
create mode 100644 gcc/testsuite/g++.dg/cpp1y/constexpr-elision1.C
diff --git a/gcc/cp/constexpr.cc b/gcc/cp/constexpr.cc
index 0dc94d9445d..5f7fc6f8f0c 100644
--- a/gcc/cp/constexpr.cc
+++ b/gcc/cp/constexpr.cc
@@ -3507,6 +3507,13 @@ cxx_eval_conditional_expression (const constexpr_ctx *ctx, tree t,
val = TREE_OPERAND (t, 1);
if (TREE_CODE (t) == IF_STMT && !val)
val = void_node;
+ /* A TARGET_EXPR may be nested inside another TARGET_EXPR, but still
+ serve as the initializer for the same object as the outer TARGET_EXPR,
+ as in
+ A a = true ? A{} : A{};
+ so strip the inner TARGET_EXPR so we don't materialize a temporary. */
+ if (TREE_CODE (val) == TARGET_EXPR)
+ val = TARGET_EXPR_INITIAL (val);
return cxx_eval_constant_expression (ctx, val, lval, non_constant_p,
overflow_p, jump_target);
}
diff --git a/gcc/testsuite/g++.dg/cpp0x/constexpr-elision1.C b/gcc/testsuite/g++.dg/cpp0x/constexpr-elision1.C
new file mode 100644
index 00000000000..9e7b9ec3405
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/constexpr-elision1.C
@@ -0,0 +1,16 @@
+// PR c++/105550
+// { dg-do compile { target c++11 } }
+
+template <typename, typename> struct pair {
+ constexpr pair(int, int) {}
+};
+template <typename _Tp, typename _Compare>
+pair<const _Tp &, const _Tp &> minmax(const _Tp &__a, const _Tp &__b,
+ _Compare) {
+ return 0 ? pair<const _Tp &, const _Tp &>(__b, __a)
+ : pair<const _Tp &, const _Tp &>(__a, __b);
+}
+typedef int value_type;
+typedef int compare_type;
+template pair<const value_type &, const value_type &>
+minmax(const value_type &, const value_type &, compare_type);
diff --git a/gcc/testsuite/g++.dg/cpp1y/constexpr-elision1.C b/gcc/testsuite/g++.dg/cpp1y/constexpr-elision1.C
new file mode 100644
index 00000000000..b225ae29cde
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp1y/constexpr-elision1.C
@@ -0,0 +1,53 @@
+// PR c++/105550
+// { dg-do compile { target c++14 } }
+
+struct A {
+ const A *p = this;
+};
+
+struct B {
+ const B *p = this;
+ constexpr operator A() const { return {}; }
+};
+
+constexpr A
+bar (A)
+{
+ return {};
+}
+
+constexpr A baz() { return {}; }
+
+struct E {
+ A a1 = true ? A{} : A{};
+ A a2 = true ? A{} : B{};
+ A a3 = false ? A{} : B{};
+ A a4 = false ? B{} : B{};
+ A a5 = A{};
+ A a6 = B{};
+ A a7 = false ? B{} : (true ? A{} : A{});
+ A a8 = false ? (true ? A{} : B{}) : (true ? A{} : A{});
+ A a9 = (A{});
+ A a10 = (true, A{});
+ A a11 = bar (A{});
+ A a12 = baz ();
+ A a13 = (A{}, A{});
+};
+
+constexpr E e{};
+
+constexpr A a1 = true ? A{} : A{};
+constexpr A a2 = true ? A{} : B{};
+constexpr A a3 = false ? A{} : B{};
+constexpr A a4 = false ? B{} : B{};
+constexpr A a5 = A{};
+constexpr A a6 = B{};
+constexpr A a7 = false ? B{} : (true ? A{} : A{});
+constexpr A a8 = false ? (true ? A{} : B{}) : (true ? A{} : A{});
+constexpr A a9 = (A{});
+constexpr A a10 = (true, A{});
+constexpr A a11 = bar (A{});
+//static_assert(a10.p == &a10, ""); // bug, 105619
+constexpr A a12 = baz ();
+//static_assert(a11.p == &a11, ""); // bug, 105619
+constexpr A a13 = (A{}, A{});
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C
index dc6492c1b0b..5e66bdd2370 100644
--- a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr16.C
@@ -40,9 +40,8 @@ struct E {
A d = true ? (false ? A{} : A{}) : (false ? A{} : A{});
};
-// FIXME: When fixing this, also fix nsdmi-aggr17.C.
-constexpr E e; // { dg-bogus "" "PR105550" { xfail *-*-* } }
-SA (e.a.p == &e.a); // { dg-bogus "" "PR105550" { xfail *-*-* } }
+constexpr E e;
+SA (e.a.p == &e.a);
E e1 = { };
diff --git a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C
index fc27a2cdac7..ca9637b37eb 100644
--- a/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C
+++ b/gcc/testsuite/g++.dg/cpp1y/nsdmi-aggr17.C
@@ -56,9 +56,8 @@ struct F {
A a = true ? A{x} : A{x};
};
-// FIXME: Doesn't work due to PR105550.
-//constexpr F f;
-//SA (f.a.p == &f.a);
+constexpr F f;
+SA (f.a.p == &f.a);
SA (e.x == 42);
F f2 = { };
F f3 = { 42 };
base-commit: 113844d68e94f4e9c0e946db351ba7d3d4a1335a
--
2.36.1
next prev parent reply other threads:[~2022-06-25 0:30 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-05-26 15:01 [PATCH] " Marek Polacek
2022-06-02 21:08 ` Jason Merrill
2022-06-25 0:30 ` Marek Polacek [this message]
2022-07-01 15:48 ` [PATCH v2] " Jason Merrill
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=YrZXHg4qzuzp3anZ@redhat.com \
--to=polacek@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=jason@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).