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);
       }
       break;
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 62f8d80c781..cb2ea8db18d 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)
@@ -2598,11 +2599,15 @@ is_convertible (tree from, tree to, bool 
explain/*=false*/)
 bool
 is_nothrow_convertible (tree from, tree to, bool explain/*=false*/)
 {
+  /* as with is_nothrow_xible.  */
+  int errs = errorcount + sorrycount;
+
   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)
+  if (explain && errs == (errorcount + sorrycount))
     {
       gcc_assert (!is_noexcept);
       explain_not_noexcept (expr);
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