diff options
author | Nathaniel Shead <nathanieloshead@gmail.com> | 2025-07-29 22:20:32 +1000 |
---|---|---|
committer | Nathaniel Shead <nathanieloshead@gmail.com> | 2025-07-31 07:55:32 +1000 |
commit | bfb8615031a8c23df814d011567735093e3c1ac6 (patch) | |
tree | dc80d86d3c4de2202e94c7a0cb23fceeb329e3a4 /gcc | |
parent | ec7244e6e09654a2e720d60ceb0f24c6d66c44f7 (diff) | |
download | gcc-bfb8615031a8c23df814d011567735093e3c1ac6.zip gcc-bfb8615031a8c23df814d011567735093e3c1ac6.tar.gz gcc-bfb8615031a8c23df814d011567735093e3c1ac6.tar.bz2 |
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 <nathanieloshead@gmail.com>
Reviewed-by: Patrick Palka <ppalka@redhat.com>
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/cp/constraint.cc | 3 | ||||
-rw-r--r-- | gcc/cp/except.cc | 10 | ||||
-rw-r--r-- | gcc/cp/method.cc | 33 | ||||
-rw-r--r-- | gcc/testsuite/g++.dg/ext/is_invocable7.C | 21 | ||||
-rw-r--r-- | 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 d4a83e4..cbdfafc 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 2c1ef4c..204769f 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 62f8d80..397e496 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 0000000..5c852fc --- /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 0000000..0ce8fb8 --- /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 } |