aboutsummaryrefslogtreecommitdiff
path: root/gcc
diff options
context:
space:
mode:
authorJason Merrill <jason@redhat.com>2015-10-08 10:42:02 -0400
committerJason Merrill <jason@gcc.gnu.org>2015-10-08 10:42:02 -0400
commit3c769e5a5bd0b684cbf3c7d115e1fe325f79e468 (patch)
tree3070e76486ff4b40bbefeae75396bb1d09301e3e /gcc
parent25a2c75d49ab2bebbae48b3640c999c43c8375bf (diff)
downloadgcc-3c769e5a5bd0b684cbf3c7d115e1fe325f79e468.zip
gcc-3c769e5a5bd0b684cbf3c7d115e1fe325f79e468.tar.gz
gcc-3c769e5a5bd0b684cbf3c7d115e1fe325f79e468.tar.bz2
re PR c++/67557 (Calling copy constructor of base class in constructor of derived class produces crashing code)
PR c++/67557 * call.c (is_base_field_ref): New. (unsafe_copy_elision_p): New. (build_over_call): Use it. From-SVN: r228602
Diffstat (limited to 'gcc')
-rw-r--r--gcc/cp/ChangeLog7
-rw-r--r--gcc/cp/call.c37
-rw-r--r--gcc/testsuite/g++.dg/init/elide3.C50
3 files changed, 93 insertions, 1 deletions
diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog
index d465ef6..bf9b0c2 100644
--- a/gcc/cp/ChangeLog
+++ b/gcc/cp/ChangeLog
@@ -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
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 93e28dc..f8db2df 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -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
index 0000000..7eb0389
--- /dev/null
+++ b/gcc/testsuite/g++.dg/init/elide3.C
@@ -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;
+}