re PR c++/67557 (Calling copy constructor of base class in constructor of derived...
authorJason Merrill <jason@redhat.com>
Thu, 8 Oct 2015 14:42:02 +0000 (10:42 -0400)
committerJason Merrill <jason@gcc.gnu.org>
Thu, 8 Oct 2015 14:42:02 +0000 (10:42 -0400)
PR c++/67557

* call.c (is_base_field_ref): New.
(unsafe_copy_elision_p): New.
(build_over_call): Use it.

From-SVN: r228602

gcc/cp/ChangeLog
gcc/cp/call.c
gcc/testsuite/g++.dg/init/elide3.C [new file with mode: 0644]

index d465ef6a5e2e30ee611b7e9f7cfd4a0a869ffc0d..bf9b0c299717d03ca835dc60249c7e122576fd84 100644 (file)
@@ -1,3 +1,10 @@
+2015-10-07  Jason Merrill  <jason@redhat.com>
+
+       PR c++/67557
+       * call.c (is_base_field_ref): New.
+       (unsafe_copy_elision_p): New.
+       (build_over_call): Use it.
+
 2015-10-07  Marek Polacek  <polacek@redhat.com>
 
        PR sanitizer/67867
index 93e28dcadd6fa5ed5c99ea85e5865663fc12d842..f8db2df7cdd1007c25235694d52f0e0dec916ee6 100644 (file)
@@ -7094,6 +7094,39 @@ call_copy_ctor (tree a, tsubst_flags_t complain)
   return r;
 }
 
+/* Return true iff T refers to a base field.  */
+
+static bool
+is_base_field_ref (tree t)
+{
+  STRIP_NOPS (t);
+  if (TREE_CODE (t) == ADDR_EXPR)
+    t = TREE_OPERAND (t, 0);
+  if (TREE_CODE (t) == COMPONENT_REF)
+    t = TREE_OPERAND (t, 1);
+  if (TREE_CODE (t) == FIELD_DECL)
+    return DECL_FIELD_IS_BASE (t);
+  return false;
+}
+
+/* We can't elide a copy from a function returning by value to a base
+   subobject, as the callee might clobber tail padding.  Return true iff this
+   could be that case.  */
+
+static bool
+unsafe_copy_elision_p (tree target, tree exp)
+{
+  tree type = TYPE_MAIN_VARIANT (TREE_TYPE (exp));
+  if (type == CLASSTYPE_AS_BASE (type))
+    return false;
+  if (!is_base_field_ref (target)
+      && resolves_to_fixed_type_p (target, NULL))
+    return false;
+  tree init = TARGET_EXPR_INITIAL (exp);
+  return (TREE_CODE (init) == AGGR_INIT_EXPR
+         && !AGGR_INIT_VIA_CTOR_P (init));
+}
+
 /* Subroutine of the various build_*_call functions.  Overload resolution
    has chosen a winning candidate CAND; build up a CALL_EXPR accordingly.
    ARGS is a TREE_LIST of the unconverted arguments to the call.  FLAGS is a
@@ -7513,7 +7546,9 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
          else if (trivial)
            return force_target_expr (DECL_CONTEXT (fn), arg, complain);
        }
-      else if (TREE_CODE (arg) == TARGET_EXPR || trivial)
+      else if (trivial
+              || (TREE_CODE (arg) == TARGET_EXPR
+                  && !unsafe_copy_elision_p (fa, arg)))
        {
          tree to = stabilize_reference (cp_build_indirect_ref (fa, RO_NULL,
                                                                complain));
diff --git a/gcc/testsuite/g++.dg/init/elide3.C b/gcc/testsuite/g++.dg/init/elide3.C
new file mode 100644 (file)
index 0000000..7eb0389
--- /dev/null
@@ -0,0 +1,50 @@
+// PR c++/67557
+// { dg-do run }
+
+namespace std
+{
+  struct string
+  {
+    typedef unsigned long size_type;
+    const char* _M_p;
+    char        _M_local_buf[1];
+
+    string(const char* s) : _M_p(_M_local_buf)
+    {
+      __builtin_printf("%p constructed\n", this);
+    }
+
+    string(const string& s) : _M_p(_M_local_buf)
+    {
+      __builtin_printf("%p copied from %p\n", this, &s);
+    }
+
+    ~string()
+    {
+      __builtin_printf("%p destroyed\n", this);
+      if (_M_p != _M_local_buf)
+       __builtin_abort();
+    }
+  };
+}
+
+struct StartTag
+{
+  explicit StartTag(std::string const & tag) : tag_(tag), keepempty_(false) {}
+  std::string tag_;
+  bool keepempty_;
+};
+
+StartTag fontToStartTag() { return StartTag(""); }
+
+struct FontTag : public StartTag
+{
+  FontTag() : StartTag(fontToStartTag()) {}
+};
+
+int main()
+{
+  FontTag x;
+  __builtin_printf("%p x.tag_ in main()\n", &x.tag_);
+  return 0;
+}