c++: Explicit constructor called in copy-initialization [PR90320]
authorMarek Polacek <polacek@redhat.com>
Thu, 23 Apr 2020 00:12:47 +0000 (20:12 -0400)
committerMarek Polacek <polacek@redhat.com>
Mon, 27 Apr 2020 01:08:41 +0000 (21:08 -0400)
commitfeb801f62239528bca2cfb6c3abd70d434b69c0a
treee5b98b248b797bf5a63855496422ea0c647d4708
parentc80863570664e7754ba1a4b8c1df6a19b2589920
c++: Explicit constructor called in copy-initialization [PR90320]

This test is rejected with a bogus "use of deleted function" error
starting with r225705 whereby convert_like_real/ck_base no longer
sets LOOKUP_ONLYCONVERTING for user_conv_p conversions.  This does
not seem to be always correct.  To recap, when we have something like
T t = x where T is a class type and the type of x is not T or derived
from T, we perform copy-initialization, something like:
  1. choose a user-defined conversion to convert x to T, the result is
     a prvalue,
  2. use this prvalue to direct-initialize t.

In the second step, explicit constructors should be considered, since
we're direct-initializing.  This is what r225705 fixed.

In this PR we are dealing with the first step, I think, where explicit
constructors should be skipped.  [over.match.copy] says "The converting
constructors of T are candidate functions" which clearly eliminates
explicit constructors.  But we also have to copy-initialize the argument
we are passing to such a converting constructor, and here we should
disregard explicit constructors too.

In this testcase we have

  V v = m;

and we choose V::V(M) to convert m to V.  But we wrongly choose
the explicit M::M<M&>(M&) to copy-initialize the argument; it's
a better match for a non-const lvalue than the implicit M::M(const M&)
but because it's explicit, we shouldn't use it.

When convert_like is processing the ck_user conversion -- the convfn is
V::V(M) -- it can see that cand->flags contains LOOKUP_ONLYCONVERTING,
but then when we're in build_over_call for this convfn, we have no way
to pass the flag to convert_like for the argument 'm', because convert_like
doesn't take flags.  Fixed by creating a new conversion flag, copy_init_p,
set in ck_base/ck_rvalue to signal that explicit constructors should be
skipped.

LOOKUP_COPY_PARM looks relevant, but again, it's a LOOKUP_* flag, so
can't pass it to convert_like.  DR 899 also seemed related, but that
deals with direct-init contexts only.

PR c++/90320
* call.c (struct conversion): Add copy_init_p.
(standard_conversion): Set copy_init_p in ck_base and ck_rvalue
if FLAGS demands LOOKUP_ONLYCONVERTING.
(convert_like_real) <case ck_base>: If copy_init_p is set, or
LOOKUP_ONLYCONVERTING into FLAGS.

* g++.dg/cpp0x/explicit13.C: New test.
* g++.dg/cpp0x/explicit14.C: New test.
gcc/cp/ChangeLog
gcc/cp/call.c
gcc/testsuite/ChangeLog
gcc/testsuite/g++.dg/cpp0x/explicit13.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp0x/explicit14.C [new file with mode: 0644]