From: Jason Merrill <jason@redhat.com>
To: Patrick Palka <ppalka@redhat.com>, gcc-patches@gcc.gnu.org
Subject: Re: [PATCH] c++: shortcut bad reference bindings [PR94894]
Date: Mon, 18 Jul 2022 16:44:09 -0400 [thread overview]
Message-ID: <33107706-023b-d00a-69b0-93dc39889534@redhat.com> (raw)
In-Reply-To: <20220718165911.3559847-1-ppalka@redhat.com>
On 7/18/22 12:59, Patrick Palka wrote:
> In case of l/rvalue or cv-qual mismatch during reference binding, we try
> to give more helpful diagnostics by attempting a bad conversion that
> ignores the mismatch. But in doing so, we may end up instantiating an
> ill-formed conversion function, something that would otherwise be
> avoided if we didn't try for the better diagnostic in the first place.
> We could just give up on producing a good diagnostic in this case, but
> ideally we could keep the good diagnostics while avoiding unnecessary
> template instantiations.
>
> To that end, this patch adapts the bad conversion shortcutting mechanism
> from r12-3346-g47543e5f9d1fc5 to handle this situation as well. The main
> observation from there applies here as well: when there's a strictly
> viable candidate, we don't care about the distinction between an unviable
> and non-strictly viable candidate. And in turn it also means we don't
> really care about the distinction between an invalid and bad conversion.
> Of course, we don't know whether there's a strictly viable candidate until
> after the fact, so we still need to keep track of when we "shortcutted"
> distinguishing between an invalid and bad conversion. This patch adds a
> new kind of conversion, ck_shortcutted, to represent such conversions.
>
> Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look OK for
> trunk?
>
> PR c++/94894
> PR c++/105766
> PR c++/106201
>
> gcc/cp/ChangeLog:
>
> * call.cc (enum conversion_kind): Add ck_shortcutted enumerator.
> (has_next): Return false for it.
> (reference_binding): Return a ck_shortcutted conversion instead
> of an actual bad conversion when LOOKUP_SHORTCUT_BAD_CONVS is set.
> Remove now obsolete early exit for the incomplete target type case.
> (implicit_conversion_1): Don't mask out LOOKUP_SHORTCUT_BAD_CONVS.
> (add_function_candidate): Set LOOKUP_SHORTCUT_BAD_CONVS iff
> shortcut_bad_convs.
> (missing_conversion_p): Return true for a ck_shortcutted
> conversion.
> * cp-tree.h (LOOKUP_SHORTCUT_BAD_CONVS): Define.
>
> gcc/testsuite/ChangeLog:
>
> * g++.dg/conversion/ref8.C: New test.
> * g++.dg/conversion/ref9.C: New test.
> ---
> gcc/cp/call.cc | 87 ++++++++++++++++----------
> gcc/cp/cp-tree.h | 5 ++
> gcc/testsuite/g++.dg/conversion/ref8.C | 22 +++++++
> gcc/testsuite/g++.dg/conversion/ref9.C | 21 +++++++
> 4 files changed, 103 insertions(+), 32 deletions(-)
> create mode 100644 gcc/testsuite/g++.dg/conversion/ref8.C
> create mode 100644 gcc/testsuite/g++.dg/conversion/ref9.C
>
> diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
> index fc98552fda2..ca2f5fbca39 100644
> --- a/gcc/cp/call.cc
> +++ b/gcc/cp/call.cc
> @@ -59,7 +59,8 @@ enum conversion_kind {
> ck_ambig,
> ck_list,
> ck_aggr,
> - ck_rvalue
> + ck_rvalue,
> + ck_shortcutted,
Please give this a comment explaining how it's used. OK with that change.
Maybe ck_deferred_bad?
> };
>
> /* The rank of the conversion. Order of the enumerals matters; better
> @@ -775,7 +776,8 @@ has_next (conversion_kind code)
> return !(code == ck_identity
> || code == ck_ambig
> || code == ck_list
> - || code == ck_aggr);
> + || code == ck_aggr
> + || code == ck_shortcutted);
> }
>
> static conversion *
> @@ -1912,18 +1914,38 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
> difference in top-level cv-qualification is subsumed by the
> initialization itself and does not constitute a conversion. */
>
> + bool maybe_valid_p = true;
> +
> /* [dcl.init.ref]
>
> Otherwise, the reference shall be an lvalue reference to a
> non-volatile const type, or the reference shall be an rvalue
> - reference.
> + reference. */
> + if (!CP_TYPE_CONST_NON_VOLATILE_P (to) && !TYPE_REF_IS_RVALUE (rto))
> + maybe_valid_p = false;
>
> - We try below to treat this as a bad conversion to improve diagnostics,
> - but if TO is an incomplete class, we need to reject this conversion
> - now to avoid unnecessary instantiation. */
> - if (!CP_TYPE_CONST_NON_VOLATILE_P (to) && !TYPE_REF_IS_RVALUE (rto)
> - && !COMPLETE_TYPE_P (to))
> - return NULL;
> + /* [dcl.init.ref]
> +
> + Otherwise, a temporary of type "cv1 T1" is created and
> + initialized from the initializer expression using the rules for a
> + non-reference copy initialization. If T1 is reference-related to
> + T2, cv1 must be the same cv-qualification as, or greater
> + cv-qualification than, cv2; otherwise, the program is ill-formed. */
> + if (related_p && !at_least_as_qualified_p (to, from))
> + maybe_valid_p = false;
> +
> + /* We try below to treat an invalid reference binding as a bad conversion
> + to improve diagnostics, but doing so may cause otherwise unnecessary
> + instantiations that can lead to hard error. So during the first pass
> + of overload resolution wherein we shortcut bad conversions, instead just
> + produce a special conversion that we'll use to indicate we might need to
> + perform a second pass if there's no strictly viable candidate. */
> + if (!maybe_valid_p && (flags & LOOKUP_SHORTCUT_BAD_CONVS))
> + {
> + conv = alloc_conversion (ck_shortcutted);
> + conv->bad_p = true;
> + return conv;
> + }
>
> /* We're generating a temporary now, but don't bind any more in the
> conversion (specifically, don't slice the temporary returned by a
> @@ -1967,7 +1989,9 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
> sflags, complain);
> if (!new_second)
> return NULL;
> - return merge_conversion_sequences (t, new_second);
> + conv = merge_conversion_sequences (t, new_second);
> + gcc_assert (maybe_valid_p || conv->bad_p);
> + return conv;
> }
> }
>
> @@ -1976,24 +2000,7 @@ reference_binding (tree rto, tree rfrom, tree expr, bool c_cast_p, int flags,
> creation of a temporary. */
> conv->need_temporary_p = true;
> conv->rvaluedness_matches_p = TYPE_REF_IS_RVALUE (rto);
> -
> - /* [dcl.init.ref]
> -
> - Otherwise, the reference shall be an lvalue reference to a
> - non-volatile const type, or the reference shall be an rvalue
> - reference. */
> - if (!CP_TYPE_CONST_NON_VOLATILE_P (to) && !TYPE_REF_IS_RVALUE (rto))
> - conv->bad_p = true;
> -
> - /* [dcl.init.ref]
> -
> - Otherwise, a temporary of type "cv1 T1" is created and
> - initialized from the initializer expression using the rules for a
> - non-reference copy initialization. If T1 is reference-related to
> - T2, cv1 must be the same cv-qualification as, or greater
> - cv-qualification than, cv2; otherwise, the program is ill-formed. */
> - if (related_p && !at_least_as_qualified_p (to, from))
> - conv->bad_p = true;
> + conv->bad_p |= !maybe_valid_p;
>
> return conv;
> }
> @@ -2015,7 +2022,8 @@ implicit_conversion_1 (tree to, tree from, tree expr, bool c_cast_p,
> resolution, or after we've chosen one. */
> flags &= (LOOKUP_ONLYCONVERTING|LOOKUP_NO_CONVERSION|LOOKUP_COPY_PARM
> |LOOKUP_NO_TEMP_BIND|LOOKUP_NO_RVAL_BIND|LOOKUP_PREFER_RVALUE
> - |LOOKUP_NO_NARROWING|LOOKUP_PROTECT|LOOKUP_NO_NON_INTEGRAL);
> + |LOOKUP_NO_NARROWING|LOOKUP_PROTECT|LOOKUP_NO_NON_INTEGRAL
> + |LOOKUP_SHORTCUT_BAD_CONVS);
>
> /* FIXME: actually we don't want warnings either, but we can't just
> have 'complain &= ~(tf_warning|tf_error)' because it would cause
> @@ -2433,6 +2441,11 @@ add_function_candidate (struct z_candidate **candidates,
> if (! viable)
> goto out;
>
> + if (shortcut_bad_convs)
> + flags |= LOOKUP_SHORTCUT_BAD_CONVS;
> + else
> + flags &= ~LOOKUP_SHORTCUT_BAD_CONVS;
> +
> /* Third, for F to be a viable function, there shall exist for each
> argument an implicit conversion sequence that converts that argument
> to the corresponding parameter of F. */
> @@ -6038,14 +6051,24 @@ perfect_candidate_p (z_candidate *cand)
> return true;
> }
>
> -/* True iff one of CAND's argument conversions is NULL. */
> +/* True iff one of CAND's argument conversions is missing. */
>
> static bool
> missing_conversion_p (const z_candidate *cand)
> {
> for (unsigned i = 0; i < cand->num_convs; ++i)
> - if (!cand->convs[i])
> - return true;
> + {
> + conversion *conv = cand->convs[i];
> + if (!conv)
> + return true;
> + if (conv->kind == ck_shortcutted)
> + {
> + /* We don't know whether this conversion is outright invalid or
> + just bad, so be conservative. */
> + gcc_checking_assert (conv->bad_p);
> + return true;
> + }
> + }
> return false;
> }
>
> diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
> index bec98aa2ac3..f3b7da2549c 100644
> --- a/gcc/cp/cp-tree.h
> +++ b/gcc/cp/cp-tree.h
> @@ -5874,6 +5874,11 @@ enum overload_flags { NO_SPECIAL = 0, DTOR_FLAG, TYPENAME_FLAG };
> #define LOOKUP_REVERSED (LOOKUP_REWRITTEN << 1)
> /* We're initializing an aggregate from a parenthesized list of values. */
> #define LOOKUP_AGGREGATE_PAREN_INIT (LOOKUP_REVERSED << 1)
> +/* We're computing conversions as part of a first pass of overload resolution
> + wherein we don't try to distinguish an unviable candidate from a
> + non-strictly viable candidate and thus can avoid computing unnecessary
> + conversions. */
> +#define LOOKUP_SHORTCUT_BAD_CONVS (LOOKUP_AGGREGATE_PAREN_INIT << 1)
>
> /* These flags are used by the conversion code.
> CONV_IMPLICIT : Perform implicit conversions (standard and user-defined).
> diff --git a/gcc/testsuite/g++.dg/conversion/ref8.C b/gcc/testsuite/g++.dg/conversion/ref8.C
> new file mode 100644
> index 00000000000..e27f642690c
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/conversion/ref8.C
> @@ -0,0 +1,22 @@
> +// PR c++/105766
> +// { dg-do compile { target c++20 } }
> +
> +template<class T>
> +struct baz {
> + baz() = default;
> + baz(int) requires __is_constructible(T, int);
> +};
> +
> +struct foo;
> +
> +struct bar {
> + bar() = default;
> + bar(foo&);
> + bar(int);
> +};
> +
> +struct foo {
> + baz<bar> m_bars;
> +};
> +
> +foo a;
> diff --git a/gcc/testsuite/g++.dg/conversion/ref9.C b/gcc/testsuite/g++.dg/conversion/ref9.C
> new file mode 100644
> index 00000000000..e6dfc03cd7f
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/conversion/ref9.C
> @@ -0,0 +1,21 @@
> +// PR c++/106201
> +// { dg-do compile { target c++11 } }
> +
> +struct A {
> + template<class T, class = decltype(f(*(T*)nullptr))>
> + A(const T&);
> +};
> +
> +struct B {
> + template<class T> B(const T&);
> +};
> +
> +void f(A&);
> +void f(B);
> +
> +struct C { };
> +
> +int main() {
> + C c;
> + f(c);
> +}
next prev parent reply other threads:[~2022-07-18 20:44 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-07-18 16:59 Patrick Palka
2022-07-18 20:44 ` Jason Merrill [this message]
2022-07-19 18:27 ` Patrick Palka
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=33107706-023b-d00a-69b0-93dc39889534@redhat.com \
--to=jason@redhat.com \
--cc=gcc-patches@gcc.gnu.org \
--cc=ppalka@redhat.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).