public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] c++: canonicity of fn types w/ complex eh specs [PR115159]
@ 2024-05-21 19:36 Patrick Palka
  2024-05-21 21:09 ` Jason Merrill
  0 siblings, 1 reply; 9+ messages in thread
From: Patrick Palka @ 2024-05-21 19:36 UTC (permalink / raw)
  To: gcc-patches; +Cc: jason, Patrick Palka

Bootstrapped and regtested on x86_64-pc-linux-gnu, does this look
OK for trunk?

Alternatively, I considered fixing this by incrementing
comparing_specializations around the call to comp_except_specs in
cp_check_qualified_type, but generally for types whose identity
depends on whether comparing_specializations is set we need to
use structural equality anyway IIUC.

Or maybe it isn't right to fix this outside of modules, and we should
instead make modules cope with this cross-function function parameter
reference?  I briefly tried looking into this but didn't get very far.

-- >8 --

Here the member functions QList::g and QList::h are given the same
function type since their exception specifications are equivalent
according to cp_tree_equal.  In doing so however this means that the
type of QList::h refers to a function parameter from QList::g, which
ends up confusing modules streaming.

I'm not sure if modules can be fixed to handle this situation, but
regardless it seems weird in principle that a function parameter can
escape in such a way.  The analogous situation with a trailing return
type and decltype

  auto g(QList &other) -> decltype(f(other));
  auto h(QList &other) -> decltype(f(other));

behaves better because we don't canonicalize decltype, and so the
function types of g and h are non-canonical and therefore not shared.

In light of this, it seems natural to treat function types with complex
eh specs as non-canonical as well so that each such function declaration
is given a unique function/method type node.  The main benefit of type
canonicalization is to speed up repeated type comparisons, but it should
rare for us to repeatedly compare two otherwise compatible function
types with complex exception specifications, so foregoing canonicalization
should be harmless IIUC.  On the other hand this change simplifies the
code responsible for adjusting unparsed eh spec variants.

	PR c++/115159

gcc/cp/ChangeLog:

	* tree.cc (build_cp_fntype_variant): Don't reuse a variant with
	a complex exception specification.  Always use structural
	equality in that case.
	(fixup_deferred_exception_variants): Always use structural
	equality for adjusted variants.

gcc/testsuite/ChangeLog:

	* g++.dg/modules/noexcept-2_a.H: New test.
	* g++.dg/modules/noexcept-2_b.C: New test.
---
 gcc/cp/tree.cc                              | 70 +++++++--------------
 gcc/testsuite/g++.dg/modules/noexcept-2_a.H | 24 +++++++
 gcc/testsuite/g++.dg/modules/noexcept-2_b.C |  4 ++
 3 files changed, 51 insertions(+), 47 deletions(-)
 create mode 100644 gcc/testsuite/g++.dg/modules/noexcept-2_a.H
 create mode 100644 gcc/testsuite/g++.dg/modules/noexcept-2_b.C

diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 9d37d255d8d..7987c01520d 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -2761,16 +2761,27 @@ build_cp_fntype_variant (tree type, cp_ref_qualifier rqual,
 {
   cp_cv_quals type_quals = TYPE_QUALS (type);
 
-  if (cp_check_qualified_type (type, type, type_quals, rqual, raises, late))
-    return type;
+  /* Canonicalize the exception specification.  */
+  tree cr = flag_noexcept_type ? canonical_eh_spec (raises) : NULL_TREE;
+  /* For a complex exception specification, always create a distinct
+     non-canonical variant for simplicity.  This also prevents noexcept-specs
+     that are in terms of a function parameter from getting shared with an
+     another function.  */
+  bool complex_p = (cr && cr != noexcept_true_spec
+		    && !UNPARSED_NOEXCEPT_SPEC_P (cr));
+  if (!complex_p)
+    {
+      if (cp_check_qualified_type (type, type, type_quals, rqual, raises, late))
+	return type;
 
-  tree v = TYPE_MAIN_VARIANT (type);
-  for (; v; v = TYPE_NEXT_VARIANT (v))
-    if (cp_check_qualified_type (v, type, type_quals, rqual, raises, late))
-      return v;
+      tree v = TYPE_MAIN_VARIANT (type);
+      for (; v; v = TYPE_NEXT_VARIANT (v))
+	if (cp_check_qualified_type (v, type, type_quals, rqual, raises, late))
+	  return v;
+    }
 
   /* Need to build a new variant.  */
-  v = build_variant_type_copy (type);
+  tree v = build_variant_type_copy (type);
   if (!TYPE_DEPENDENT_P (v))
     /* We no longer know that it's not type-dependent.  */
     TYPE_DEPENDENT_P_VALID (v) = false;
@@ -2791,10 +2802,7 @@ build_cp_fntype_variant (tree type, cp_ref_qualifier rqual,
       break;
     }
 
-  /* Canonicalize the exception specification.  */
-  tree cr = flag_noexcept_type ? canonical_eh_spec (raises) : NULL_TREE;
-
-  if (TYPE_STRUCTURAL_EQUALITY_P (type))
+  if (TYPE_STRUCTURAL_EQUALITY_P (type) || complex_p)
     /* Propagate structural equality. */
     SET_TYPE_STRUCTURAL_EQUALITY (v);
   else if (TYPE_CANONICAL (type) != type || cr != raises || late)
@@ -2812,55 +2820,23 @@ build_cp_fntype_variant (tree type, cp_ref_qualifier rqual,
 /* TYPE is a function or method type with a deferred exception
    specification that has been parsed to RAISES.  Fixup all the type
    variants that are affected in place.  Via decltype &| noexcept
-   tricks, the unparsed spec could have escaped into the type system.
-   The general case is hard to fixup canonical types for.  */
+   tricks, the unparsed spec could have escaped into the type system.  */
 
 void
 fixup_deferred_exception_variants (tree type, tree raises)
 {
   tree original = TYPE_RAISES_EXCEPTIONS (type);
-  tree cr = flag_noexcept_type ? canonical_eh_spec (raises) : NULL_TREE;
 
   gcc_checking_assert (UNPARSED_NOEXCEPT_SPEC_P (original));
 
-  /* Though sucky, this walk will process the canonical variants
-     first.  */
-  tree prev = NULL_TREE;
   for (tree variant = TYPE_MAIN_VARIANT (type);
-       variant; prev = variant, variant = TYPE_NEXT_VARIANT (variant))
+       variant; variant = TYPE_NEXT_VARIANT (variant))
     if (TYPE_RAISES_EXCEPTIONS (variant) == original)
       {
 	gcc_checking_assert (variant != TYPE_MAIN_VARIANT (type));
 
-	if (!TYPE_STRUCTURAL_EQUALITY_P (variant))
-	  {
-	    cp_cv_quals var_quals = TYPE_QUALS (variant);
-	    cp_ref_qualifier rqual = type_memfn_rqual (variant);
-
-	    /* If VARIANT would become a dup (cp_check_qualified_type-wise)
-	       of an existing variant in the variant list of TYPE after its
-	       exception specification has been parsed, elide it.  Otherwise,
-	       build_cp_fntype_variant could use it, leading to "canonical
-	       types differ for identical types."  */
-	    tree v = TYPE_MAIN_VARIANT (type);
-	    for (; v; v = TYPE_NEXT_VARIANT (v))
-	      if (cp_check_qualified_type (v, variant, var_quals,
-					   rqual, cr, false))
-		{
-		  /* The main variant will not match V, so PREV will never
-		     be null.  */
-		  TYPE_NEXT_VARIANT (prev) = TYPE_NEXT_VARIANT (variant);
-		  break;
-		}
-	    TYPE_RAISES_EXCEPTIONS (variant) = raises;
-
-	    if (!v)
-	      v = build_cp_fntype_variant (TYPE_CANONICAL (variant),
-					   rqual, cr, false);
-	    TYPE_CANONICAL (variant) = TYPE_CANONICAL (v);
-	  }
-	else
-	  TYPE_RAISES_EXCEPTIONS (variant) = raises;
+	SET_TYPE_STRUCTURAL_EQUALITY (variant);
+	TYPE_RAISES_EXCEPTIONS (variant) = raises;
 
 	if (!TYPE_DEPENDENT_P (variant))
 	  /* We no longer know that it's not type-dependent.  */
diff --git a/gcc/testsuite/g++.dg/modules/noexcept-2_a.H b/gcc/testsuite/g++.dg/modules/noexcept-2_a.H
new file mode 100644
index 00000000000..b7144f42d7e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/noexcept-2_a.H
@@ -0,0 +1,24 @@
+// PR c++/115159
+// { dg-additional-options -fmodule-header }
+// { dg-module-cmi {} }
+
+struct QDebug;
+
+template<class T> void f(T);
+
+template<class T> struct QList {
+  QDebug g(QList &other) noexcept(noexcept(f(other)));
+  QDebug h(QList &other) noexcept(noexcept(f(other)));
+};
+
+struct QObjectData {
+  QList<int> children;
+};
+
+struct QIODevice {
+  QObjectData d_ptr;
+};
+
+struct QDebug {
+  QDebug(QIODevice);
+};
diff --git a/gcc/testsuite/g++.dg/modules/noexcept-2_b.C b/gcc/testsuite/g++.dg/modules/noexcept-2_b.C
new file mode 100644
index 00000000000..d34c63add10
--- /dev/null
+++ b/gcc/testsuite/g++.dg/modules/noexcept-2_b.C
@@ -0,0 +1,4 @@
+// PR c++/115159
+// { dg-additional-options "-fmodules-ts -fno-module-lazy" }
+
+import "noexcept-2_a.H";
-- 
2.45.1.216.g4365c6fcf9


^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2024-05-22 13:38 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-05-21 19:36 [PATCH] c++: canonicity of fn types w/ complex eh specs [PR115159] Patrick Palka
2024-05-21 21:09 ` Jason Merrill
2024-05-21 21:27   ` Patrick Palka
2024-05-21 21:31     ` Patrick Palka
2024-05-21 21:36     ` Jason Merrill
2024-05-22  1:55       ` Patrick Palka
2024-05-22  2:58         ` Jason Merrill
2024-05-22 13:01           ` Patrick Palka
2024-05-22 13:38             ` Jason Merrill

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).