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