On Thu, 3 Feb 2022, Jason Merrill wrote: > On 2/3/22 16:06, Patrick Palka wrote: > > On Thu, 3 Feb 2022, Jason Merrill wrote: > > > > > On 2/3/22 14:58, Patrick Palka wrote: > > > > When synthesizing a defaulted comparison op from > > > > maybe_instantiate_noexcept, we seem to be forgetting to instantiate the > > > > noexcept-spec afterwards. > > > > > > Hmm, there shouldn't be any need to instantiate the noexcept-spec > > > afterwards, > > > it should have been set by ~comp_info. > > > > It appears the comp_info class sets the noexcept-spec only if the > > comparison function hasn't been declared with an explicit noexcept-spec. > > Otherwise the class doesn't touch the noexcept-spec, and it remains a > > DEFERRED_NOEXCEPT with non-NULL DEFERRED_NOEXCEPT_PATTERN. > > Ah, I see. So perhaps we should entirely skip the current DECL_MAYBE_DELETED > handling in maybe_instantiate_noexcept if we have DEFERRED_NOEXCEPT with > non-null DEFERRED_NOEXCEPT_PATTERN (which seems to want another macro)?
Hmm, I tried something to that effect but it looks like mark_used relies solely on the DECL_MAYBE_DELETED handling in maybe_instantiate_noexcept to determine deletedness of a defaulted comparison operator (via trying to synthesize it). So by sometimes sidestepping this handling, we end up failing to diagnose the use of the deleted defaulted <=> in e.g.: #include <compare> struct A { }; template<bool B> struct X { auto operator<=>(const X&) const noexcept(B) = default; A a; }; X<true> x_t; auto c = x_t <=> x_t; // should be error: use of deleted <=> b/c A lacks <=> In light of this, I suppose mark_used should directly perform DECL_MAYBE_DELETED synthesization of its own? And it looks like DECL_MAYBE_DELETED is always false after doing maybe_synthesize_method, so I think maybe_instantiate_noexcept should return !DECL_DELETED_FN instead of !DECL_MAYBE_DELETED after synthesization. How does this look? Lightly tested so far, bootstrap and regtesting in progress. -- >8 -- PR c++/96242 gcc/cp/ChangeLog: * decl2.cc (mark_used): Directly synthesize a DECL_MAYBE_DELETED fn by calling maybe_synthesize_method instead of relying on maybe_instantiate_noexcept. * pt.cc (maybe_instantiate_noexcept): Restrict DECL_MAYBE_DELETED synthesization to only fns with an implicit noexcept-spec, and return !DECL_DELETED_FN instead of !DECL_MAYBE_DELETED afteward. gcc/testsuite/ChangeLog: * g++.dg/cpp2a/spaceship-synth15.C: New test. --- gcc/cp/decl2.cc | 17 ++++++++++---- gcc/cp/pt.cc | 11 +++++----- .../g++.dg/cpp2a/spaceship-synth15.C | 22 +++++++++++++++++++ 3 files changed, 41 insertions(+), 9 deletions(-) create mode 100644 gcc/testsuite/g++.dg/cpp2a/spaceship-synth15.C diff --git a/gcc/cp/decl2.cc b/gcc/cp/decl2.cc index a2aa5f1de4e..4d3798d02fe 100644 --- a/gcc/cp/decl2.cc +++ b/gcc/cp/decl2.cc @@ -5772,10 +5772,19 @@ mark_used (tree decl, tsubst_flags_t complain) if (TREE_CODE (decl) == CONST_DECL) used_types_insert (DECL_CONTEXT (decl)); - if (TREE_CODE (decl) == FUNCTION_DECL - && !DECL_DELETED_FN (decl) - && !maybe_instantiate_noexcept (decl, complain)) - return false; + if (TREE_CODE (decl) == FUNCTION_DECL) + { + if (DECL_MAYBE_DELETED (decl)) + { + ++function_depth; + maybe_synthesize_method (decl); + --function_depth; + } + + if (!DECL_DELETED_FN (decl) + && !maybe_instantiate_noexcept (decl, complain)) + return false; + } if (TREE_CODE (decl) == FUNCTION_DECL && DECL_DELETED_FN (decl)) diff --git a/gcc/cp/pt.cc b/gcc/cp/pt.cc index d219bba6ac1..584c752529b 100644 --- a/gcc/cp/pt.cc +++ b/gcc/cp/pt.cc @@ -25982,7 +25982,11 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) && (!flag_noexcept_type || type_dependent_expression_p (fn))) return true; - if (DECL_MAYBE_DELETED (fn)) + tree fntype = TREE_TYPE (fn); + tree spec = TYPE_RAISES_EXCEPTIONS (fntype); + + if (DECL_MAYBE_DELETED (fn) + && (!spec || UNEVALUATED_NOEXCEPT_SPEC_P (spec))) { if (fn == current_function_decl) /* We're in start_preparsed_function, keep going. */ @@ -25991,12 +25995,9 @@ maybe_instantiate_noexcept (tree fn, tsubst_flags_t complain) ++function_depth; maybe_synthesize_method (fn); --function_depth; - return !DECL_MAYBE_DELETED (fn); + return !DECL_DELETED_FN (fn); } - tree fntype = TREE_TYPE (fn); - tree spec = TYPE_RAISES_EXCEPTIONS (fntype); - if (!spec || !TREE_PURPOSE (spec)) return true; diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth15.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth15.C new file mode 100644 index 00000000000..00ea6c10474 --- /dev/null +++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth15.C @@ -0,0 +1,22 @@ +// PR c++/96242 +// { dg-do compile { target c++20 } } + +#include <compare> + +template<bool B> +struct X { + auto operator<=>(const X&) const noexcept(B) = default; + bool operator==(const X&) const noexcept(!B) = default; +}; + +X<true> x_t; +static_assert(noexcept(x_t <=> x_t)); +static_assert(noexcept(x_t < x_t)); +static_assert(!noexcept(x_t == x_t)); +static_assert(!noexcept(x_t != x_t)); + +X<false> x_f; +static_assert(!noexcept(x_f <=> x_f)); +static_assert(!noexcept(x_f < x_f)); +static_assert(noexcept(x_f == x_f)); +static_assert(noexcept(x_f != x_f)); -- 2.35.0