https://gcc.gnu.org/g:bfb8615031a8c23df814d011567735093e3c1ac6

commit r16-2652-gbfb8615031a8c23df814d011567735093e3c1ac6
Author: Nathaniel Shead <nathanielosh...@gmail.com>
Date:   Tue Jul 29 22:20:32 2025 +1000

    c++: Don't assume trait funcs return error_mark_node when tf_error is 
passed [PR121291]
    
    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>

Diff:
---
 gcc/cp/constraint.cc                               |  3 +-
 gcc/cp/except.cc                                   | 10 ++++---
 gcc/cp/method.cc                                   | 33 ++++++----------------
 gcc/testsuite/g++.dg/ext/is_invocable7.C           | 21 ++++++++++++++
 gcc/testsuite/g++.dg/ext/is_nothrow_convertible5.C | 15 ++++++++++
 5 files changed, 52 insertions(+), 30 deletions(-)

diff --git a/gcc/cp/constraint.cc b/gcc/cp/constraint.cc
index d4a83e429e50..cbdfafc90c08 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 2c1ef4c3af62..204769f489a7 100644
--- a/gcc/cp/except.cc
+++ b/gcc/cp/except.cc
@@ -1218,13 +1218,15 @@ 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)
+void
+explain_not_noexcept (tree expr)
 {
   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 62f8d80c7814..397e496ed477 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 000000000000..5c852fc8428e
--- /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 000000000000..0ce8fb821915
--- /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 }

Reply via email to