On Thu, Apr 23, 2020 at 02:48:38PM -0400, Jason Merrill wrote:
> On 4/22/20 11:27 PM, Marek Polacek wrote:
> > 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.  So I've resorted to setting need_temporary_p in
> > a ck_rvalue, thus far unused, to signal that we're only interested in
> > non-explicit constructors.
> > 
> > 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.
> > 
> > Does this make sense?
> > 
> > Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?
> > 
> >     PR c++/90320
> >     * call.c (standard_conversion): Set need_temporary_p if FLAGS demands
> >     LOOKUP_ONLYCONVERTING.
> >     (convert_like_real) <case ck_base>: If a ck_rvalue has
> >     need_temporary_p set, or LOOKUP_ONLYCONVERTING into FLAGS.
> > 
> >     * g++.dg/cpp0x/explicit13.C: New test.
> > ---
> >   gcc/cp/call.c                           | 24 +++++++++++++++++-------
> >   gcc/testsuite/g++.dg/cpp0x/explicit13.C | 14 ++++++++++++++
> >   2 files changed, 31 insertions(+), 7 deletions(-)
> >   create mode 100644 gcc/testsuite/g++.dg/cpp0x/explicit13.C
> > 
> > diff --git a/gcc/cp/call.c b/gcc/cp/call.c
> > index c58231601c9..d802f1a0c2f 100644
> > --- a/gcc/cp/call.c
> > +++ b/gcc/cp/call.c
> > @@ -92,10 +92,10 @@ struct conversion {
> >        language standards, e.g. disregarding pointer qualifiers or
> >        converting integers to pointers.  */
> >     BOOL_BITFIELD bad_p : 1;
> > -  /* If KIND is ck_ref_bind ck_base_conv, true to indicate that a
> > +  /* If KIND is ck_ref_bind or ck_base, true to indicate that a
> >        temporary should be created to hold the result of the
> > -     conversion.  If KIND is ck_ambig or ck_user, true means force
> > -     copy-initialization.  */
> > +     conversion.  If KIND is ck_ambig, ck_rvalue, or ck_user, true means
> > +     force copy-initialization.  */
> >     BOOL_BITFIELD need_temporary_p : 1;
> >     /* If KIND is ck_ptr or ck_pmem, true to indicate that a conversion
> >        from a pointer-to-derived to pointer-to-base is being performed.  */
> > @@ -1252,6 +1252,8 @@ standard_conversion (tree to, tree from, tree expr, 
> > bool c_cast_p,
> >         if (flags & LOOKUP_PREFER_RVALUE)
> >     /* Tell convert_like_real to set LOOKUP_PREFER_RVALUE.  */
> >     conv->rvaluedness_matches_p = true;
> > +      if (flags & LOOKUP_ONLYCONVERTING)
> > +   conv->need_temporary_p = true;
> 
> Presumably we want the same thing for ck_base?

We actually already set need_temporary_p for ck_base in standard_conversion:
1529       conv->need_temporary_p = !(flags & LOOKUP_NO_TEMP_BIND);

> > @@ -7654,10 +7656,18 @@ convert_like_real (conversion *convs, tree expr, 
> > tree fn, int argnum,
> >      destination [is treated as direct-initialization].  [dcl.init] */
> >         flags = LOOKUP_NORMAL;
> >         if (convs->user_conv_p)
> > -   /* This conversion is being done in the context of a user-defined
> > -      conversion (i.e. the second step of copy-initialization), so
> > -      don't allow any more.  */
> > -   flags |= LOOKUP_NO_CONVERSION;
> > +   {
> > +     /* This conversion is being done in the context of a user-defined
> > +        conversion (i.e. the second step of copy-initialization), so
> > +        don't allow any more.  */
> > +     flags |= LOOKUP_NO_CONVERSION;
> > +     /* But we might also be performing a conversion of the argument
> > +        to the user-defined conversion, i.e., not a conversion of the
> > +        result of the user-defined conversion.  In which case we skip
> > +        explicit constructors.  */
> > +     if (convs->kind == ck_rvalue && convs->need_temporary_p)
> > +       flags |= LOOKUP_ONLYCONVERTING;
> > +   }
> >         else
> >     flags |= LOOKUP_ONLYCONVERTING;
> 
> And then here set LOOKUP_ONLYCONVERTING only based on need_temporary_p
> regardless of user_conv_p?

Could do.  How's this patch?  I've included a test with a derived-to-base
conversion to exercise ck_base.

Bootstrapped/regtested on x86_64-pc-linux-gnu, ok for trunk?  Maybe 9 too
after a while.

-- >8 --
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.  So I've resorted to setting need_temporary_p in
a ck_rvalue, thus far unused, to signal that we're only interested in
non-explicit constructors.

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 (standard_conversion): Set need_temporary_p if FLAGS demands
        LOOKUP_ONLYCONVERTING.
        (convert_like_real) <case ck_base>: If need_temporary_p is set, or
        LOOKUP_ONLYCONVERTING into FLAGS.

        * g++.dg/cpp0x/explicit13.C: New test.
        * g++.dg/cpp0x/explicit14.C: New test.
---
 gcc/cp/call.c                           | 20 +++++++++++++-------
 gcc/testsuite/g++.dg/cpp0x/explicit13.C | 14 ++++++++++++++
 gcc/testsuite/g++.dg/cpp0x/explicit14.C | 16 ++++++++++++++++
 3 files changed, 43 insertions(+), 7 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/explicit13.C
 create mode 100644 gcc/testsuite/g++.dg/cpp0x/explicit14.C

diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index c58231601c9..612ca56cca4 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -92,10 +92,10 @@ struct conversion {
      language standards, e.g. disregarding pointer qualifiers or
      converting integers to pointers.  */
   BOOL_BITFIELD bad_p : 1;
-  /* If KIND is ck_ref_bind ck_base_conv, true to indicate that a
+  /* If KIND is ck_ref_bind or ck_base, true to indicate that a
      temporary should be created to hold the result of the
-     conversion.  If KIND is ck_ambig or ck_user, true means force
-     copy-initialization.  */
+     conversion.  If KIND is ck_ambig, ck_rvalue, or ck_user, true means
+     force copy-initialization.  */
   BOOL_BITFIELD need_temporary_p : 1;
   /* If KIND is ck_ptr or ck_pmem, true to indicate that a conversion
      from a pointer-to-derived to pointer-to-base is being performed.  */
@@ -1252,6 +1252,8 @@ standard_conversion (tree to, tree from, tree expr, bool 
c_cast_p,
       if (flags & LOOKUP_PREFER_RVALUE)
        /* Tell convert_like_real to set LOOKUP_PREFER_RVALUE.  */
        conv->rvaluedness_matches_p = true;
+      if (flags & LOOKUP_ONLYCONVERTING)
+       conv->need_temporary_p = true;
     }
 
    /* Allow conversion between `__complex__' data types.  */
@@ -7653,12 +7655,16 @@ convert_like_real (conversion *convs, tree expr, tree 
fn, int argnum,
         type is the same class as, or a derived class of, the class of the
         destination [is treated as direct-initialization].  [dcl.init] */
       flags = LOOKUP_NORMAL;
+      /* This conversion is being done in the context of a user-defined
+        conversion (i.e. the second step of copy-initialization), so
+        don't allow any more.  */
       if (convs->user_conv_p)
-       /* This conversion is being done in the context of a user-defined
-          conversion (i.e. the second step of copy-initialization), so
-          don't allow any more.  */
        flags |= LOOKUP_NO_CONVERSION;
-      else
+      /* We might be performing a conversion of the argument
+        to the user-defined conversion, i.e., not a conversion of the
+        result of the user-defined conversion.  In which case we skip
+        explicit constructors.  */
+      if (convs->need_temporary_p)
        flags |= LOOKUP_ONLYCONVERTING;
       if (convs->rvaluedness_matches_p)
        /* standard_conversion got LOOKUP_PREFER_RVALUE.  */
diff --git a/gcc/testsuite/g++.dg/cpp0x/explicit13.C 
b/gcc/testsuite/g++.dg/cpp0x/explicit13.C
new file mode 100644
index 00000000000..cbd9a73d8fc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/explicit13.C
@@ -0,0 +1,14 @@
+// PR c++/90320
+// { dg-do compile { target c++11 } }
+
+struct M {
+  M() = default;
+  template <typename T> explicit M(T&&) = delete;
+};
+
+struct V {
+  V(M m);
+};
+
+M m;
+V v = m;
diff --git a/gcc/testsuite/g++.dg/cpp0x/explicit14.C 
b/gcc/testsuite/g++.dg/cpp0x/explicit14.C
new file mode 100644
index 00000000000..8a8adfe1677
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp0x/explicit14.C
@@ -0,0 +1,16 @@
+// PR c++/90320
+// { dg-do compile { target c++11 } }
+
+struct B { };
+
+struct M : B {
+  M() = default;
+  template <typename T> explicit M(T&&) = delete;
+};
+
+struct V {
+  V(B);
+};
+
+M m;
+V v = m;

base-commit: 8c9d69bafc8fc1f31f6cb50dffab106641d086d0
-- 
Marek Polacek • Red Hat, Inc. • 300 A St, Boston, MA

Reply via email to