From feb801f62239528bca2cfb6c3abd70d434b69c0a Mon Sep 17 00:00:00 2001 From: Marek Polacek Date: Wed, 22 Apr 2020 20:12:47 -0400 Subject: [PATCH] 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&) 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) : 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 | 9 +++++++++ gcc/cp/call.c | 26 ++++++++++++++++++++----- gcc/testsuite/ChangeLog | 6 ++++++ gcc/testsuite/g++.dg/cpp0x/explicit13.C | 14 +++++++++++++ gcc/testsuite/g++.dg/cpp0x/explicit14.C | 16 +++++++++++++++ 5 files changed, 66 insertions(+), 5 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/ChangeLog b/gcc/cp/ChangeLog index 32408f7056b..4e0ea91883d 100644 --- a/gcc/cp/ChangeLog +++ b/gcc/cp/ChangeLog @@ -1,3 +1,12 @@ +2020-04-22 Marek Polacek + + 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) : If copy_init_p is set, or + LOOKUP_ONLYCONVERTING into FLAGS. + 2020-04-26 Iain Sandoe PR c++/94752 diff --git a/gcc/cp/call.c b/gcc/cp/call.c index c58231601c9..8345997ccf9 100644 --- a/gcc/cp/call.c +++ b/gcc/cp/call.c @@ -92,7 +92,7 @@ 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. */ @@ -111,6 +111,10 @@ struct conversion { /* Whether check_narrowing should only check TREE_CONSTANTs; used in build_converted_constant_expr. */ BOOL_BITFIELD check_narrowing_const_only: 1; + /* True if this conversion is taking place in a copy-initialization context + and we should only consider converting constructors. Only set in + ck_base and ck_rvalue. */ + BOOL_BITFIELD copy_init_p : 1; /* The type of the expression resulting from the conversion. */ tree type; union { @@ -1252,6 +1256,10 @@ 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 we're performing copy-initialization, remember to skip + explicit constructors. */ + if (flags & LOOKUP_ONLYCONVERTING) + conv->copy_init_p = true; } /* Allow conversion between `__complex__' data types. */ @@ -1528,6 +1536,10 @@ 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 we're performing copy-initialization, remember to skip + explicit constructors. */ + if (flags & LOOKUP_ONLYCONVERTING) + conv->copy_init_p = true; } else return NULL; @@ -7653,12 +7665,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->copy_init_p) flags |= LOOKUP_ONLYCONVERTING; if (convs->rvaluedness_matches_p) /* standard_conversion got LOOKUP_PREFER_RVALUE. */ diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index d6529ea7d1f..1e3325853e1 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,3 +1,9 @@ +2020-04-22 Marek Polacek + + PR c++/90320 + * g++.dg/cpp0x/explicit13.C: New test. + * g++.dg/cpp0x/explicit14.C: New test. + 2020-04-27 Iain Buclaw PR d/89418 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 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 explicit M(T&&) = delete; +}; + +struct V { + V(B); +}; + +M m; +V v = m; -- 2.30.2