Commit dcfa8518 by Jason Merrill Committed by Jason Merrill

PR c++/86521 - C++17 copy elision in initialization by constructor.

This is an overlooked case in C++17 mandatory copy elision: We want overload
resolution to reflect that initializing an object from a prvalue does not
involve a copy or move constructor even when [over.match.ctor] says that
only constructors are candidates.  Here I implement that by looking through
the copy/move constructor in joust.

	* call.c (joust_maybe_elide_copy): New.
	(joust): Call it.

From-SVN: r269667
parent edaff7c9
2019-03-13 Jason Merrill <jason@redhat.com>
PR c++/86521 - C++17 copy elision in initialization by constructor.
* call.c (joust_maybe_elide_copy): New.
(joust): Call it.
2019-03-13 Marek Polacek <polacek@redhat.com> 2019-03-13 Marek Polacek <polacek@redhat.com>
PR c++/88979 - further P0634 fix for constructors. PR c++/88979 - further P0634 fix for constructors.
......
...@@ -10508,6 +10508,33 @@ add_warning (struct z_candidate *winner, struct z_candidate *loser) ...@@ -10508,6 +10508,33 @@ add_warning (struct z_candidate *winner, struct z_candidate *loser)
winner->warnings = cw; winner->warnings = cw;
} }
/* CAND is a constructor candidate in joust in C++17 and up. If it copies a
prvalue returned from a conversion function, replace CAND with the candidate
for the conversion and return true. Otherwise, return false. */
static bool
joust_maybe_elide_copy (z_candidate *&cand)
{
tree fn = cand->fn;
if (!DECL_COPY_CONSTRUCTOR_P (fn) && !DECL_MOVE_CONSTRUCTOR_P (fn))
return false;
conversion *conv = cand->convs[0];
gcc_checking_assert (conv->kind == ck_ref_bind);
conv = next_conversion (conv);
if (conv->kind == ck_user && !TYPE_REF_P (conv->type))
{
gcc_checking_assert (same_type_ignoring_top_level_qualifiers_p
(conv->type, DECL_CONTEXT (fn)));
z_candidate *uc = conv->cand;
if (DECL_CONV_FN_P (uc->fn))
{
cand = uc;
return true;
}
}
return false;
}
/* Compare two candidates for overloading as described in /* Compare two candidates for overloading as described in
[over.match.best]. Return values: [over.match.best]. Return values:
...@@ -10588,6 +10615,27 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, ...@@ -10588,6 +10615,27 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
} }
} }
/* Handle C++17 copy elision in [over.match.ctor] (direct-init) context. The
standard currently says that only constructors are candidates, but if one
copies a prvalue returned by a conversion function we want to treat the
conversion as the candidate instead.
Clang does something similar, as discussed at
http://lists.isocpp.org/core/2017/10/3166.php
http://lists.isocpp.org/core/2019/03/5721.php */
int elided_tiebreaker = 0;
if (len == 1 && cxx_dialect >= cxx17
&& DECL_P (cand1->fn)
&& DECL_COMPLETE_CONSTRUCTOR_P (cand1->fn)
&& !(cand1->flags & LOOKUP_ONLYCONVERTING))
{
bool elided1 = joust_maybe_elide_copy (cand1);
bool elided2 = joust_maybe_elide_copy (cand2);
/* As a tiebreaker below we will prefer a constructor to a conversion
operator exposed this way. */
elided_tiebreaker = elided2 - elided1;
}
for (i = 0; i < len; ++i) for (i = 0; i < len; ++i)
{ {
conversion *t1 = cand1->convs[i + off1]; conversion *t1 = cand1->convs[i + off1];
...@@ -10697,6 +10745,11 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn, ...@@ -10697,6 +10745,11 @@ joust (struct z_candidate *cand1, struct z_candidate *cand2, bool warn,
if (winner) if (winner)
return winner; return winner;
/* Put this tiebreaker first, so that we don't try to look at second_conv of
a constructor candidate that doesn't have one. */
if (elided_tiebreaker)
return elided_tiebreaker;
/* DR 495 moved this tiebreaker above the template ones. */ /* DR 495 moved this tiebreaker above the template ones. */
/* or, if not that, /* or, if not that,
the context is an initialization by user-defined conversion (see the context is an initialization by user-defined conversion (see
......
...@@ -17,5 +17,5 @@ struct Source { ...@@ -17,5 +17,5 @@ struct Source {
int main() { int main() {
Source x; Source x;
Dest d(move(x)); // { dg-error "ambiguous" } Dest d(move(x)); // { dg-error "ambiguous" "" { target c++14_down } }
} }
// PR c++/81311 // PR c++/81311
// { dg-do link } // { dg-do compile { target c++11 } }
struct function struct function
{ {
...@@ -8,12 +8,12 @@ struct function ...@@ -8,12 +8,12 @@ struct function
struct ref struct ref
{ {
operator function&() const; operator function&() const = delete;
} r; } r;
struct val struct val
{ {
operator function() const; operator function() const = delete;
} v; } v;
int main() int main()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment