diff mbox

C++ PATCH for c++/79533 (C++17 ICE with cast to reference)

Message ID CADzB+2nvX3QwGP44B_Q96h1mE32yyjFazZLD266u9pjnRGAJVA@mail.gmail.com
State New
Headers show

Commit Message

Jason Merrill Feb. 17, 2017, 2:05 p.m. UTC
The old copy elision code in build_over_call checks to make sure that
we don't hit it for a temporary object in C++17, where it should have
been handled at a higher level.  We were hitting it for this testcase
because the compiler was looking through the cast to reference type to
find the temporary inside, and trying to elide the copy.

I believe that this behavior is (and has been) wrong for all dialects;
in C++14 and below copy elision from a temporary talks about "a
temporary class object _that has not been bound to a reference_", and
the static_cast binds the temporary to a reference.  So this patch
looks through the outermost conversion to reference type, which comes
from binding to the parameter of the copy conversion, but stops at any
earlier conversion to reference type.

Tested x86_64-pc-linux-gnu, applying to trunk.
commit 96ec73b986fe122ac306cdefb227499aa5b5221a
Author: Jason Merrill <jason@redhat.com>
Date:   Thu Feb 16 16:17:33 2017 -0500

            PR c++/79533 - C++17 ICE with temporary cast to reference
    
            * call.c (build_over_call): Conversion to a reference prevents copy
            elision.
diff mbox

Patch

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 154509b..4ef444b 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -7955,7 +7955,14 @@  build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
 
       /* Pull out the real argument, disregarding const-correctness.  */
       targ = arg;
-      while (CONVERT_EXPR_P (targ)
+      /* Strip the reference binding for the constructor parameter.  */
+      if (CONVERT_EXPR_P (targ)
+	  && TREE_CODE (TREE_TYPE (targ)) == REFERENCE_TYPE)
+	targ = TREE_OPERAND (targ, 0);
+      /* But don't strip any other reference bindings; binding a temporary to a
+	 reference prevents copy elision.  */
+      while ((CONVERT_EXPR_P (targ)
+	      && TREE_CODE (TREE_TYPE (targ)) != REFERENCE_TYPE)
 	     || TREE_CODE (targ) == NON_LVALUE_EXPR)
 	targ = TREE_OPERAND (targ, 0);
       if (TREE_CODE (targ) == ADDR_EXPR)
diff --git a/gcc/testsuite/g++.dg/init/elide6.C b/gcc/testsuite/g++.dg/init/elide6.C
new file mode 100644
index 0000000..d40bd9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/elide6.C
@@ -0,0 +1,11 @@ 
+// PR c++/79533
+
+struct S {
+  S();
+  S(const S&);
+};
+S f();
+S s(static_cast<S const &>(f()));
+
+// The static_cast prevents copy elision.
+// { dg-final { scan-assembler "_ZN1SC1ERKS_" } }