c++: Fix copy elision for base initialization
While working on PR98642 I noticed that in this testcase we were eliding the copy, calling the complete default constructor to initialize the B base subobject, and therefore wrongly initializing the non-existent A subobject of B. The test doesn't care whether the copy is elided or not, but checks that we are actually calling a base constructor for B. The patch preserves the elision, but changes the initializer to call the base constructor instead of the complete constructor. gcc/cp/ChangeLog: * call.c (base_ctor_for, make_base_init_ok): New. (build_over_call): Use make_base_init_ok. gcc/testsuite/ChangeLog: * g++.dg/cpp1z/elide4.C: New test.
This commit is contained in:
parent
ad26034338
commit
424deca72b
@ -8425,6 +8425,60 @@ call_copy_ctor (tree a, tsubst_flags_t complain)
|
||||
return r;
|
||||
}
|
||||
|
||||
/* Return the base constructor corresponding to COMPLETE_CTOR or NULL_TREE. */
|
||||
|
||||
static tree
|
||||
base_ctor_for (tree complete_ctor)
|
||||
{
|
||||
tree clone;
|
||||
FOR_EACH_CLONE (clone, DECL_CLONED_FUNCTION (complete_ctor))
|
||||
if (DECL_BASE_CONSTRUCTOR_P (clone))
|
||||
return clone;
|
||||
return NULL_TREE;
|
||||
}
|
||||
|
||||
/* Try to make EXP suitable to be used as the initializer for a base subobject,
|
||||
and return whether we were successful. EXP must have already been cleared
|
||||
by unsafe_copy_elision_p. */
|
||||
|
||||
static bool
|
||||
make_base_init_ok (tree exp)
|
||||
{
|
||||
if (TREE_CODE (exp) == TARGET_EXPR)
|
||||
exp = TARGET_EXPR_INITIAL (exp);
|
||||
while (TREE_CODE (exp) == COMPOUND_EXPR)
|
||||
exp = TREE_OPERAND (exp, 1);
|
||||
if (TREE_CODE (exp) == COND_EXPR)
|
||||
{
|
||||
bool ret = make_base_init_ok (TREE_OPERAND (exp, 2));
|
||||
if (tree op1 = TREE_OPERAND (exp, 1))
|
||||
{
|
||||
bool r1 = make_base_init_ok (op1);
|
||||
/* If unsafe_copy_elision_p was false, the arms should match. */
|
||||
gcc_assert (r1 == ret);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
if (TREE_CODE (exp) != AGGR_INIT_EXPR)
|
||||
/* A trivial copy is OK. */
|
||||
return true;
|
||||
if (!AGGR_INIT_VIA_CTOR_P (exp))
|
||||
/* unsafe_copy_elision_p must have said this is OK. */
|
||||
return true;
|
||||
tree fn = cp_get_callee_fndecl_nofold (exp);
|
||||
if (DECL_BASE_CONSTRUCTOR_P (fn))
|
||||
return true;
|
||||
gcc_assert (DECL_COMPLETE_CONSTRUCTOR_P (fn));
|
||||
fn = base_ctor_for (fn);
|
||||
if (!fn || DECL_HAS_IN_CHARGE_PARM_P (fn))
|
||||
/* The base constructor has more parameters, so we can't just change the
|
||||
call target. It would be possible to splice in the appropriate
|
||||
arguments, but probably not worth the complexity. */
|
||||
return false;
|
||||
AGGR_INIT_EXPR_FN (exp) = build_address (fn);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Return true iff T refers to a base or potentially-overlapping field, which
|
||||
cannot be used for return by invisible reference. We avoid doing C++17
|
||||
mandatory copy elision when this is true.
|
||||
@ -9152,6 +9206,10 @@ build_over_call (struct z_candidate *cand, int flags, tsubst_flags_t complain)
|
||||
else
|
||||
cp_warn_deprecated_use (fn, complain);
|
||||
|
||||
if (eliding_temp && DECL_BASE_CONSTRUCTOR_P (fn)
|
||||
&& !make_base_init_ok (arg))
|
||||
unsafe = true;
|
||||
|
||||
/* If we're creating a temp and we already have one, don't create a
|
||||
new one. If we're not creating a temp but we get one, use
|
||||
INIT_EXPR to collapse the temp into our target. Otherwise, if the
|
||||
|
||||
24
gcc/testsuite/g++.dg/cpp1z/elide4.C
Normal file
24
gcc/testsuite/g++.dg/cpp1z/elide4.C
Normal file
@ -0,0 +1,24 @@
|
||||
// { dg-do compile { target c++11 } }
|
||||
|
||||
// Check that there's a call to some base constructor of B: either the default
|
||||
// constructor, if the copy is elided, or the copy constructor.
|
||||
|
||||
// { dg-final { scan-assembler {call[ \t]*_?_ZN1BC2} { target { i?86-*-* x86_64-*-* } } } }
|
||||
|
||||
int count;
|
||||
struct A { int i = count++; };
|
||||
struct B: virtual A {
|
||||
B() { }
|
||||
B(const B& b);
|
||||
};
|
||||
bool x;
|
||||
struct C: B
|
||||
{
|
||||
C() : B(x ? (0,B()) : B()) { }
|
||||
};
|
||||
|
||||
int main()
|
||||
{
|
||||
C c;
|
||||
return count;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user