https://gcc.gnu.org/g:3c98d06a9016a0fa3a806879bd168f13b8a606f8

commit r15-779-g3c98d06a9016a0fa3a806879bd168f13b8a606f8
Author: Patrick Palka <ppa...@redhat.com>
Date:   Wed May 22 17:45:04 2024 -0400

    c++: canonicity of fn types w/ complex eh specs [PR115159]
    
    Here the member functions QList::g and QList::h are given the same
    function type by build_cp_fntype_variant since their noexcept-specs are
    equivalent according to cp_tree_equal.  In doing so however this means
    that the function 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
    noexcept-specs as non-canonical as well so that each such function
    declaration is given a unique function type node.  (The main benefit of
    type canonicalization is to speed up repeated type comparisons, but it
    should be rare to repeatedly compare two otherwise compatible function
    types with complex noexcept-specs.)
    
    To that end, this patch strengthens the ce_exact case of comp_except_specs
    to require identity instead of equivalence of the noexcept-spec so that
    build_cp_fntype_variant doesn't reuse a variant when it shouldn't.  In
    turn we need to use structural equality for types with a complex eh spec.
    This lets us get rid of the tricky handling of canonical types when updating
    unparsed noexcept-spec variants.
    
            PR c++/115159
    
    gcc/cp/ChangeLog:
    
            * tree.cc (build_cp_fntype_variant): Always use structural
            equality for types with a complex exception specification.
            (fixup_deferred_exception_variants): Use structural equality
            for adjusted variants.
            * typeck.cc (comp_except_specs): Require == instead of
            cp_tree_equal for ce_exact noexcept-spec comparison.
    
    gcc/testsuite/ChangeLog:
    
            * g++.dg/modules/noexcept-2_a.H: New test.
            * g++.dg/modules/noexcept-2_b.C: New test.
    
    Reviewed-by: Jason Merrill <ja...@redhat.com>

Diff:
---
 gcc/cp/tree.cc                              | 48 ++++++-----------------------
 gcc/cp/typeck.cc                            |  4 ++-
 gcc/testsuite/g++.dg/modules/noexcept-2_a.H | 24 +++++++++++++++
 gcc/testsuite/g++.dg/modules/noexcept-2_b.C |  4 +++
 4 files changed, 41 insertions(+), 39 deletions(-)

diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 9d37d255d8d..4d87661b4ad 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -2793,9 +2793,13 @@ build_cp_fntype_variant (tree type, cp_ref_qualifier 
rqual,
 
   /* Canonicalize the exception specification.  */
   tree cr = flag_noexcept_type ? canonical_eh_spec (raises) : NULL_TREE;
+  bool complex_eh_spec_p = (cr && cr != noexcept_true_spec
+                           && !UNPARSED_NOEXCEPT_SPEC_P (cr));
 
-  if (TYPE_STRUCTURAL_EQUALITY_P (type))
-    /* Propagate structural equality. */
+  if (TYPE_STRUCTURAL_EQUALITY_P (type) || complex_eh_spec_p)
+    /* Propagate structural equality.  And always use structural equality
+       for function types with a complex noexcept-spec since their identity
+       may depend on e.g. whether comparing_specializations is set.  */
     SET_TYPE_STRUCTURAL_EQUALITY (v);
   else if (TYPE_CANONICAL (type) != type || cr != raises || late)
     /* Build the underlying canonical type, since it is different
@@ -2812,55 +2816,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/cp/typeck.cc b/gcc/cp/typeck.cc
index 5f16994300f..d7fa6e0dd96 100644
--- a/gcc/cp/typeck.cc
+++ b/gcc/cp/typeck.cc
@@ -1227,7 +1227,9 @@ comp_except_specs (const_tree t1, const_tree t2, int 
exact)
   if ((t1 && TREE_PURPOSE (t1))
       || (t2 && TREE_PURPOSE (t2)))
     return (t1 && t2
-           && cp_tree_equal (TREE_PURPOSE (t1), TREE_PURPOSE (t2)));
+           && (exact == ce_exact
+               ? TREE_PURPOSE (t1) == TREE_PURPOSE (t2)
+               : cp_tree_equal (TREE_PURPOSE (t1), TREE_PURPOSE (t2))));
 
   if (t1 == NULL_TREE)                    /* T1 is ...  */
     return t2 == NULL_TREE || exact == ce_derived;
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";

Reply via email to