public inbox for gcc-cvs@sourceware.org
help / color / mirror / Atom feed
* [gcc r13-1397] c++: fix broken copy elision with nested TARGET_EXPRs [PR105550]
@ 2022-07-01 16:11 Marek Polacek
  0 siblings, 0 replies; only message in thread
From: Marek Polacek @ 2022-07-01 16:11 UTC (permalink / raw)
  To: gcc-cvs

https://gcc.gnu.org/g:ecd11acacd6be57af930fa617d7c31ecb40e7f74

commit r13-1397-gecd11acacd6be57af930fa617d7c31ecb40e7f74
Author: Marek Polacek <polacek@redhat.com>
Date:   Wed May 25 18:11:31 2022 -0400

    c++: fix broken copy elision with nested TARGET_EXPRs [PR105550]
    
    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.

Diff:
---
 gcc/cp/constexpr.cc                             |  7 ++++
 gcc/testsuite/g++.dg/cpp0x/constexpr-elision1.C | 16 ++++++++
 gcc/testsuite/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(-)

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 };


^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2022-07-01 16:11 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-07-01 16:11 [gcc r13-1397] c++: fix broken copy elision with nested TARGET_EXPRs [PR105550] Marek Polacek

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).