On Wed, 30 Jul 2025, Nathaniel Shead wrote:

> On Tue, Jul 29, 2025 at 03:45:29PM -0400, Patrick Palka wrote:
> > On Tue, 29 Jul 2025, Nathaniel Shead wrote:
> > 
> > > Tested on x86_64-pc-linux-gnu, OK for trunk if full bootstrap+regtest
> > > passes?
> > > 
> > > -- >8 --
> > > 
> > > For the sake of determining if there are other errors in user code to
> > > report early, many trait functions don't return error_mark_node if not
> > > called in a SFINAE context (i.e., tf_error is set).  This patch removes
> > > some assumptions on this behaviour I'd made when improving diagnostics
> > > of builtin traits.
> > > 
> > >   PR c++/121291
> > > 
> > > gcc/cp/ChangeLog:
> > > 
> > >   * constraint.cc (diagnose_trait_expr): Remove assumption about
> > >   failures returning error_mark_node.
> > >   * method.cc (build_invoke): Adjust comment.
> > >   (is_nothrow_convertible): Check for errorcount changes.
> > > 
> > > gcc/testsuite/ChangeLog:
> > > 
> > >   * g++.dg/ext/is_invocable7.C: New test.
> > >   * g++.dg/ext/is_nothrow_convertible5.C: New test.
> > > 
> > > Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> > > ---
> > >  gcc/cp/constraint.cc                          |  9 +++++---
> > >  gcc/cp/method.cc                              |  9 ++++++--
> > >  gcc/testsuite/g++.dg/ext/is_invocable7.C      | 21 +++++++++++++++++++
> > >  .../g++.dg/ext/is_nothrow_convertible5.C      | 15 +++++++++++++
> > >  4 files changed, 49 insertions(+), 5 deletions(-)
> > >  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable7.C
> > >  create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_convertible5.C
> > > 
> > > diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> > > index d4a83e429e5..94238c9e8f1 100644
> > > --- a/gcc/cp/constraint.cc
> > > +++ b/gcc/cp/constraint.cc
> > > @@ -3176,8 +3176,7 @@ diagnose_trait_expr (location_t loc, tree expr, 
> > > tree args)
> > >     inform (loc, "%qT is not invocable, because", t1);
> > >   else
> > >     inform (loc, "%qT is not invocable by %qT, because", t1, t2);
> > > - tree call = build_invoke (t1, t2, tf_error);
> > > - gcc_assert (call == error_mark_node);
> > > + build_invoke (t1, t2, tf_error);
> > >        }
> > >        break;
> > >      case CPTK_IS_LAYOUT_COMPATIBLE:
> > > @@ -3221,8 +3220,12 @@ diagnose_trait_expr (location_t loc, tree expr, 
> > > tree args)
> > >     inform (loc, "%qT is not nothrow invocable, because", t1);
> > >   else
> > >     inform (loc, "%qT is not nothrow invocable by %qT, because", t1, t2);
> > > + /* We can't rely on build_invoke returning error_mark_node, because
> > > +    when passing tf_error in some cases a valid call may be returned
> > > +    anyway.  */
> > > + int errs = errorcount + sorrycount;
> > >   tree call = build_invoke (t1, t2, tf_error);
> > > - if (call != error_mark_node)
> > > + if (call != error_mark_node && errs == errorcount + sorrycount)
> > >     explain_not_noexcept (call);
> > 
> > What do you think about just checking expr_noexcept_p here instead (or
> > removing the assert in explain_not_noexcept)?  So that if a call is not
> > nothrow invocable because the selected candidate is private _and_
> > because it's not noexcept, we'd report both errors.  I see that
> > tsubst_compound_requirement already behaves this way.
> 
> Good point, I think I like this more; it's definitely the less complex
> behaviour and is also potentially useful.  Here's an adjusted patch that
> does this; bootstrapped and regtested on x86_64-pc-linux-gnu, OK for
> trunk?
> 
> -- >8 --
> 
> For the sake of determining if there are other errors in user code to
> report early, many trait functions don't always return error_mark_node
> if not called in a SFINAE context (i.e., tf_error is set).  This patch
> removes some assumptions on this behaviour I'd made when improving
> diagnostics of builtin traits.
> 
>       PR c++/121291
> 
> gcc/cp/ChangeLog:
> 
>       * constraint.cc (diagnose_trait_expr): Remove assumption about
>       failures returning error_mark_node.
>       * except.cc (explain_not_noexcept): Allow expr not being
>       noexcept.
>       * method.cc (build_invoke): Adjust comment.
>       (is_trivially_xible): Always note non-trivial components if expr
>       is not null or error_mark_node.
>       (is_nothrow_xible): Likewise for non-noexcept components.
>       (is_nothrow_convertible): Likewise.
> 
> gcc/testsuite/ChangeLog:
> 
>       * g++.dg/ext/is_invocable7.C: New test.
>       * g++.dg/ext/is_nothrow_convertible5.C: New test.
> 
> Signed-off-by: Nathaniel Shead <nathanielosh...@gmail.com>
> Reviewed-by: Patrick Palka <ppa...@redhat.com>
> ---
>  gcc/cp/constraint.cc                          |  3 +-
>  gcc/cp/except.cc                              |  7 ++--
>  gcc/cp/method.cc                              | 33 +++++--------------
>  gcc/testsuite/g++.dg/ext/is_invocable7.C      | 21 ++++++++++++
>  .../g++.dg/ext/is_nothrow_convertible5.C      | 15 +++++++++
>  5 files changed, 50 insertions(+), 29 deletions(-)
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_invocable7.C
>  create mode 100644 gcc/testsuite/g++.dg/ext/is_nothrow_convertible5.C
> 
> diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
> index d4a83e429e5..cbdfafc90c0 100644
> --- a/gcc/cp/constraint.cc
> +++ b/gcc/cp/constraint.cc
> @@ -3176,8 +3176,7 @@ diagnose_trait_expr (location_t loc, tree expr, tree 
> args)
>         inform (loc, "%qT is not invocable, because", t1);
>       else
>         inform (loc, "%qT is not invocable by %qT, because", t1, t2);
> -     tree call = build_invoke (t1, t2, tf_error);
> -     gcc_assert (call == error_mark_node);
> +     build_invoke (t1, t2, tf_error);
>        }
>        break;
>      case CPTK_IS_LAYOUT_COMPATIBLE:
> diff --git a/gcc/cp/except.cc b/gcc/cp/except.cc
> index 2c1ef4c3af6..c0b93df9456 100644
> --- a/gcc/cp/except.cc
> +++ b/gcc/cp/except.cc
> @@ -1218,13 +1218,14 @@ expr_noexcept_p (tree expr, tsubst_flags_t complain)
>      return true;
>  }
>  
> -/* Explain why EXPR is not noexcept.  */
> +/* If EXPR is not noexcept, explain why.  */
>  
>  void explain_not_noexcept (tree expr)

While we're changing this function could you also fix the formatting
here (newline b/t return type and function name)?  OK with that change,
thanks!

>  {
>    tree fn = cp_walk_tree_without_duplicates (&expr, check_noexcept_r, 0);
> -  gcc_assert (fn);
> -  if (DECL_P (fn))
> +  if (!fn)
> +    /* The call was noexcept, nothing to do.  */;
> +  else if (DECL_P (fn))
>      inform (DECL_SOURCE_LOCATION (fn), "%qD is not %<noexcept%>", fn);
>    else
>      inform (location_of (fn), "%qT is not %<noexcept%>", TREE_TYPE (fn));
> diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
> index 62f8d80c781..397e496ed47 100644
> --- a/gcc/cp/method.cc
> +++ b/gcc/cp/method.cc
> @@ -1952,7 +1952,8 @@ build_trait_object (tree type, tsubst_flags_t complain)
>  }
>  
>  /* [func.require] Build an expression of INVOKE(FN_TYPE, ARG_TYPES...).  If 
> the
> -   given is not invocable, returns error_mark_node.  */
> +   given is not invocable, returns error_mark_node, unless COMPLAIN includes
> +   tf_error.  */
>  
>  tree
>  build_invoke (tree fn_type, const_tree arg_types, tsubst_flags_t complain)
> @@ -2460,21 +2461,13 @@ bool
>  is_trivially_xible (enum tree_code code, tree to, tree from,
>                   bool explain/*=false*/)
>  {
> -  /* In some cases, when producing errors is_xible_helper may not return
> -     error_mark_node, so check if it looks like we've already emitted any
> -     diagnostics to ensure we don't do so multiple times.  */
> -  int errs = errorcount + sorrycount;
> -
>    tree expr = is_xible_helper (code, to, from, explain);
>    if (expr == NULL_TREE || expr == error_mark_node)
>      return false;
>  
>    tree nt = cp_walk_tree_without_duplicates (&expr, check_nontriv, NULL);
> -  if (explain && errs == (errorcount + sorrycount))
> -    {
> -      gcc_assert (nt);
> -      inform (location_of (nt), "%qE is non-trivial", nt);
> -    }
> +  if (explain && nt)
> +    inform (location_of (nt), "%qE is non-trivial", nt);
>    return !nt;
>  }
>  
> @@ -2487,9 +2480,6 @@ bool
>  is_nothrow_xible (enum tree_code code, tree to, tree from,
>                 bool explain/*=false*/)
>  {
> -  /* As with is_trivially_xible.  */
> -  int errs = errorcount + sorrycount;
> -
>    ++cp_noexcept_operand;
>    tree expr = is_xible_helper (code, to, from, explain);
>    --cp_noexcept_operand;
> @@ -2497,11 +2487,8 @@ is_nothrow_xible (enum tree_code code, tree to, tree 
> from,
>      return false;
>  
>    bool is_noexcept = expr_noexcept_p (expr, tf_none);
> -  if (explain && errs == (errorcount + sorrycount))
> -    {
> -      gcc_assert (!is_noexcept);
> -      explain_not_noexcept (expr);
> -    }
> +  if (explain && !is_noexcept)
> +    explain_not_noexcept (expr);
>    return is_noexcept;
>  }
>  
> @@ -2601,12 +2588,10 @@ is_nothrow_convertible (tree from, tree to, bool 
> explain/*=false*/)
>    tree expr = is_convertible_helper (from, to, explain);
>    if (expr == NULL_TREE || expr == error_mark_node)
>      return false;
> +
>    bool is_noexcept = expr_noexcept_p (expr, tf_none);
> -  if (explain)
> -    {
> -      gcc_assert (!is_noexcept);
> -      explain_not_noexcept (expr);
> -    }
> +  if (explain && !is_noexcept)
> +    explain_not_noexcept (expr);
>    return is_noexcept;
>  }
>  
> diff --git a/gcc/testsuite/g++.dg/ext/is_invocable7.C 
> b/gcc/testsuite/g++.dg/ext/is_invocable7.C
> new file mode 100644
> index 00000000000..5c852fc8428
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_invocable7.C
> @@ -0,0 +1,21 @@
> +// PR c++/121291
> +// { dg-do compile { target c++17 } }
> +
> +template <typename T>
> +constexpr bool is_invocable = __is_invocable(T);
> +
> +template <typename T>
> +constexpr bool is_nothrow_invocable = __is_nothrow_invocable(T);
> +
> +struct S {
> +private:
> +  int operator()() noexcept;  // { dg-message "here" }
> +};
> +
> +static_assert(is_invocable<S>);  // { dg-error "assert" }
> +// { dg-message "not invocable" "" { target *-*-* } .-1 }
> +// { dg-error "private within this context" "" { target *-*-* } .-2 }
> +
> +static_assert(is_nothrow_invocable<S>);  // { dg-error "assert" }
> +// { dg-message "not nothrow invocable" "" { target *-*-* } .-1 }
> +// { dg-error "private within this context" "" { target *-*-* } .-2 }
> diff --git a/gcc/testsuite/g++.dg/ext/is_nothrow_convertible5.C 
> b/gcc/testsuite/g++.dg/ext/is_nothrow_convertible5.C
> new file mode 100644
> index 00000000000..0ce8fb82191
> --- /dev/null
> +++ b/gcc/testsuite/g++.dg/ext/is_nothrow_convertible5.C
> @@ -0,0 +1,15 @@
> +// PR c++/121291
> +// { dg-do compile { target c++17 } }
> +
> +template <typename T, typename U>
> +constexpr bool is_nothrow_convertible = __is_nothrow_convertible(T, U);
> +
> +struct A {};
> +struct B {
> +private:
> +  operator A() noexcept;  // { dg-message "here" }
> +};
> +
> +static_assert(is_nothrow_convertible<B, A>);  // { dg-error "assert" }
> +// { dg-message "not nothrow convertible" "" { target *-*-* } .-1 }
> +// { dg-error "private within this context" "" { target *-*-* } .-2 }
> -- 
> 2.47.0
> 
> 

Reply via email to