From 3c769e5a5bd0b684cbf3c7d115e1fe325f79e468 Mon Sep 17 00:00:00 2001 From: Jason Merrill Date: Thu, 8 Oct 2015 10:42:02 -0400 Subject: [PATCH] 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 --- gcc/cp/ChangeLog | 7 +++++ gcc/cp/call.c | 37 +++++++++++++++++++++- gcc/testsuite/g++.dg/init/elide3.C | 50 ++++++++++++++++++++++++++++++ 3 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 gcc/testsuite/g++.dg/init/elide3.C diff --git a/gcc/cp/ChangeLog b/gcc/cp/ChangeLog index d465ef6a5e2..bf9b0c29971 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,10 @@ +2015-10-07 Jason Merrill + + 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 PR sanitizer/67867 diff --git a/gcc/cp/call.c b/gcc/cp/call.c index 93e28dcadd6..f8db2df7cdd 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 00000000000..7eb0389a3f6 --- /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; +} -- 2.30.2