c++: dependent noexcept-spec on defaulted comparison op [PR96242]
authorPatrick Palka <ppalka@redhat.com>
Sun, 6 Feb 2022 15:47:48 +0000 (10:47 -0500)
committerPatrick Palka <ppalka@redhat.com>
Sun, 6 Feb 2022 15:47:48 +0000 (10:47 -0500)
Here we're failing to instantiate the defaulted comparison op's
explicit dependent noexcept-spec.  The problem is ultimately that
mark_used relies on maybe_instantiate_noexcept to synthesize a defaulted
comparison op, but the relevant DECL_MAYBE_DELETED fn handling in m_i_n
is intended for such functions whose noexcept-spec wasn't explicitly
provided (and is therefore determined via synthesis), so m_i_n just
exits early afterwards, without considering that the synthesized fn may
have an explicit noexcept-spec that needs instantiating.

This patch fixes this issue by making mark_used directly synthesize a
DECL_MAYBE_DELETED fn before calling maybe_instantiate_noexcept.  And
in turn, we can properly restrict the DECL_MAYBE_DELETED fn synthesis
in m_i_n to only those without an explicit noexcept-spec.

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.  Move call to m_i_n after the
DECL_DELETED_FN handling.
* pt.cc (maybe_instantiate_noexcept): Restrict DECL_MAYBE_DELETED
fn synthesis to only those with an implicit noexcept-spec, and
return !DECL_DELETED_FN instead of !DECL_MAYBE_DELETED afterwards.

gcc/testsuite/ChangeLog:

* g++.dg/cpp2a/spaceship-synth15.C: New test.

gcc/cp/decl2.cc
gcc/cp/pt.cc
gcc/testsuite/g++.dg/cpp2a/spaceship-synth15.C [new file with mode: 0644]

index a2aa5f1..7890833 100644 (file)
@@ -5772,27 +5772,34 @@ 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 (TREE_CODE (decl) == FUNCTION_DECL
-      && DECL_DELETED_FN (decl))
-    {
-      if (DECL_ARTIFICIAL (decl)
-         && DECL_CONV_FN_P (decl)
-         && LAMBDA_TYPE_P (DECL_CONTEXT (decl)))
-       /* We mark a lambda conversion op as deleted if we can't
-          generate it properly; see maybe_add_lambda_conv_op.  */
-       sorry ("converting lambda that uses %<...%> to function pointer");
-      else if (complain & tf_error)
+      if (DECL_DELETED_FN (decl))
        {
-         error ("use of deleted function %qD", decl);
-         if (!maybe_explain_implicit_delete (decl))
-           inform (DECL_SOURCE_LOCATION (decl), "declared here");
+         if (DECL_ARTIFICIAL (decl)
+             && DECL_CONV_FN_P (decl)
+             && LAMBDA_TYPE_P (DECL_CONTEXT (decl)))
+           /* We mark a lambda conversion op as deleted if we can't
+              generate it properly; see maybe_add_lambda_conv_op.  */
+           sorry ("converting lambda that uses %<...%> to function pointer");
+         else if (complain & tf_error)
+           {
+             error ("use of deleted function %qD", decl);
+             if (!maybe_explain_implicit_delete (decl))
+               inform (DECL_SOURCE_LOCATION (decl), "declared here");
+           }
+         return false;
        }
-      return false;
+
+      if (!maybe_instantiate_noexcept (decl, complain))
+       return false;
     }
 
   if (VAR_OR_FUNCTION_DECL_P (decl) && DECL_LOCAL_DECL_P (decl))
index f640612..c7af471 100644 (file)
@@ -25981,7 +25981,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 ((!spec || UNEVALUATED_NOEXCEPT_SPEC_P (spec))
+      && DECL_MAYBE_DELETED (fn))
     {
       if (fn == current_function_decl)
        /* We're in start_preparsed_function, keep going.  */
@@ -25990,12 +25994,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 (file)
index 0000000..00ea6c1
--- /dev/null
@@ -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));