]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++/modules: Ensure deduction guides are always reachable [PR115231]
authorNathaniel Shead <nathanieloshead@gmail.com>
Sat, 15 Jun 2024 12:50:14 +0000 (22:50 +1000)
committerNathaniel Shead <nathanieloshead@gmail.com>
Wed, 7 Aug 2024 01:49:47 +0000 (11:49 +1000)
Deduction guides are represented as 'normal' functions currently, and
have no special handling in modules.  However, this causes some issues;
by [temp.deduct.guide] a deduction guide is not found by normal name
lookup and instead all reachable deduction guides for a class template
should be considered, but this does not happen currently.

To solve this, this patch ensures that all deduction guides are
considered exported to ensure that they are always visible to importers
if they are reachable.  Another alternative here would be to add a new
kind of "all reachable" flag to name lookup, but that is complicated by
some difficulties in handling GM entities; this may be a better way to
go if more kinds of entities end up needing this handling, however.

Another issue here is that because deduction guides are "unrelated"
functions, they will usually get discarded from the GMF, so this patch
ensures that when finding dependencies, GMF deduction guides will also
have bindings created.  We do this in find_dependencies so that we don't
unnecessarily create bindings for GMF deduction guides that are never
reached; for consistency we do this for *all* deduction guides, not just
GM ones.  We also make sure that the opposite (a deduction guide being
the only purview reference to a GMF template) correctly marks it as
reachable.

Finally, when merging deduction guides from multiple modules, the name
lookup code may now return two-dimensional overload sets, so update
callers to match.

As a small drive-by improvement this patch also updates the error pretty
printing code to add a space before the '->' when printing a deduction
guide, so we get 'S(int) -> S<int>' instead of 'S(int)-> S<int>'.

PR c++/115231

gcc/cp/ChangeLog:

* error.cc (dump_function_decl): Add a space before '->' when
printing deduction guides.
* module.cc (depset::hash::add_binding_entity): Don't create
bindings for guides here, only mark dependencies.
(depset::hash::add_deduction_guides): New.
(depset::hash::find_dependencies): Add deduction guide
dependencies for a class template.
(module_state::write_cluster): Always consider deduction guides
as exported.
* pt.cc (deduction_guides_for): Use 'lkp_iterator' instead of
'ovl_iterator'.

gcc/testsuite/ChangeLog:

* g++.dg/modules/dguide-1_a.C: New test.
* g++.dg/modules/dguide-1_b.C: New test.
* g++.dg/modules/dguide-2_a.C: New test.
* g++.dg/modules/dguide-2_b.C: New test.
* g++.dg/modules/dguide-3_a.C: New test.
* g++.dg/modules/dguide-3_b.C: New test.
* g++.dg/modules/dguide-3_c.C: New test.
* g++.dg/modules/dguide-3_d.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
gcc/cp/error.cc
gcc/cp/module.cc
gcc/cp/pt.cc
gcc/testsuite/g++.dg/modules/dguide-1_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/dguide-1_b.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/dguide-2_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/dguide-2_b.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/dguide-3_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/dguide-3_b.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/dguide-3_c.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/dguide-3_d.C [new file with mode: 0644]

index ee3868efaed6269b6df29dd9b01be496b5f5da6b..6c22ff55b46391be4490c0c3cdf58b90857d4363 100644 (file)
@@ -1936,6 +1936,7 @@ dump_function_decl (cxx_pretty_printer *pp, tree t, int flags)
        dump_type_suffix (pp, ret, flags);
       else if (deduction_guide_p (t))
        {
+         pp->set_padding (pp_before);
          pp_cxx_ws_string (pp, "->");
          dump_type (pp, TREE_TYPE (TREE_TYPE (t)), flags);
        }
index 7130faf26f52f6ddfd9c95fe5aac81a0a8d4a994..723f0890d96e0d64e6c8b23a6130c9336e07f285 100644 (file)
@@ -2589,6 +2589,9 @@ public:
     void add_partial_entities (vec<tree, va_gc> *);
     void add_class_entities (vec<tree, va_gc> *);
 
+  private:
+    void add_deduction_guides (tree decl);
+
   public:    
     void find_dependencies (module_state *);
     bool finalize_dependencies ();
@@ -13172,6 +13175,15 @@ depset::hash::add_binding_entity (tree decl, WMB_Flags flags, void *data_)
        /* Ignore NTTP objects.  */
        return false;
 
+      if (deduction_guide_p (decl))
+       {
+         /* Ignore deduction guides, bindings for them will be created within
+            find_dependencies for their class template.  But still build a dep
+            for them so that we don't discard them.  */
+         data->hash->make_dependency (decl, EK_FOR_BINDING);
+         return false;
+       }
+
       if (!(flags & WMB_Using) && CP_DECL_CONTEXT (decl) != data->ns)
        {
          /* An unscoped enum constant implicitly brought into the containing
@@ -13600,6 +13612,50 @@ find_pending_key (tree decl, tree *decl_p = nullptr)
   return ns;
 }
 
+/* Creates bindings and dependencies for all deduction guides of
+   the given class template DECL as needed.  */
+
+void
+depset::hash::add_deduction_guides (tree decl)
+{
+  /* Alias templates never have deduction guides.  */
+  if (DECL_ALIAS_TEMPLATE_P (decl))
+    return;
+
+  /* We don't need to do anything for class-scope deduction guides,
+     as they will be added as members anyway.  */
+  if (!DECL_NAMESPACE_SCOPE_P (decl))
+    return;
+
+  tree ns = CP_DECL_CONTEXT (decl);
+  tree name = dguide_name (decl);
+
+  /* We always add all deduction guides with a given name at once,
+     so if there's already a binding there's nothing to do.  */
+  if (find_binding (ns, name))
+    return;
+
+  tree guides = lookup_qualified_name (ns, name, LOOK_want::NORMAL,
+                                      /*complain=*/false);
+  if (guides == error_mark_node)
+    return;
+
+  /* We have bindings to add.  */
+  depset *binding = make_binding (ns, name);
+  add_namespace_context (binding, ns);
+
+  depset **slot = binding_slot (ns, name, /*insert=*/true);
+  *slot = binding;
+
+  for (lkp_iterator it (guides); it; ++it)
+    {
+      gcc_checking_assert (!TREE_VISITED (*it));
+      depset *dep = make_dependency (*it, EK_FOR_BINDING);
+      binding->deps.safe_push (dep);
+      dep->deps.safe_push (binding);
+    }
+}
+
 /* Iteratively find dependencies.  During the walk we may find more
    entries on the same binding that need walking.  */
 
@@ -13659,6 +13715,10 @@ depset::hash::find_dependencies (module_state *module)
                }
              walker.end ();
 
+             if (!walker.is_key_order ()
+                 && DECL_CLASS_TEMPLATE_P (decl))
+               add_deduction_guides (decl);
+
              if (!walker.is_key_order ()
                  && TREE_CODE (decl) == TEMPLATE_DECL
                  && !DECL_UNINSTANTIATED_TEMPLATE_FRIEND_P (decl))
@@ -15158,6 +15218,11 @@ module_state::write_cluster (elf_out *to, depset *scc[], unsigned size,
                      flags |= cbf_hidden;
                    else if (DECL_MODULE_EXPORT_P (STRIP_TEMPLATE (bound)))
                      flags |= cbf_export;
+                   else if (deduction_guide_p (bound))
+                     /* Deduction guides are always exported so that they are
+                        visible to name lookup whenever their class template
+                        is reachable.  */
+                     flags |= cbf_export;
                  }
 
                gcc_checking_assert (DECL_P (bound));
index 9a4ff553b2f307e8ef8e077fdbea6d05143ffa8d..2db59213c549f2b30735de3646fc6d5db2b5028e 100644 (file)
@@ -30721,7 +30721,7 @@ deduction_guides_for (tree tmpl, bool &any_dguides_p, tsubst_flags_t complain)
   else
     {
       cands = ctor_deduction_guides_for (tmpl, complain);
-      for (ovl_iterator it (guides); it; ++it)
+      for (lkp_iterator it (guides); it; ++it)
        cands = lookup_add (*it, cands);
     }
 
diff --git a/gcc/testsuite/g++.dg/modules/dguide-1_a.C b/gcc/testsuite/g++.dg/modules/dguide-1_a.C
new file mode 100644 (file)
index 0000000..834e033
--- /dev/null
@@ -0,0 +1,44 @@
+// PR c++/115231
+// { dg-additional-options "-fmodules-ts -Wno-global-module" }
+// { dg-module-cmi M }
+
+module;
+
+template <typename T>
+struct A {
+  template <typename U> A(U);
+};
+
+template <typename T> A(T) -> A<T>;
+
+export module M;
+
+// Exporting a GMF entity should make the deduction guides reachable.
+export using ::A;
+
+
+export template <typename T>
+struct B {
+  template <typename U> B(U);
+};
+
+// Not exported, but should still be reachable by [temp.deduct.guide] p1.
+B(int) -> B<double>;
+
+
+// Class-scope deduction guides should be reachable as well, even if
+// the class body was not exported.
+export template <typename T> struct C;
+
+template <typename T>
+struct C {
+  template <typename U>
+  struct I {
+    template <typename V> I(V);
+  };
+
+  I(int) -> I<int>;
+
+  template <typename P>
+  I(const P*) -> I<P>;
+};
diff --git a/gcc/testsuite/g++.dg/modules/dguide-1_b.C b/gcc/testsuite/g++.dg/modules/dguide-1_b.C
new file mode 100644 (file)
index 0000000..9726698
--- /dev/null
@@ -0,0 +1,20 @@
+// PR c++/115231
+// { dg-additional-options "-fmodules-ts" }
+
+import M;
+
+int main() {
+  // Check that deduction guides are reachable,
+  // and that they declared the right type.
+  A a(1);
+  A<int> a2 = a;
+
+  B b(2);
+  B<double> b2 = b;
+
+  C<int>::I x(10);
+  C<int>::I<int> x2 = x;
+
+  C<int>::I y("xyz");
+  C<int>::I<char> y2 = y;
+}
diff --git a/gcc/testsuite/g++.dg/modules/dguide-2_a.C b/gcc/testsuite/g++.dg/modules/dguide-2_a.C
new file mode 100644 (file)
index 0000000..fcd6c57
--- /dev/null
@@ -0,0 +1,24 @@
+// PR c++/115231
+// { dg-additional-options "-fmodules-ts -Wno-global-module" }
+// { dg-module-cmi M }
+
+module;
+
+template <typename T>
+struct A {
+  template <typename U> A(U);
+};
+template <typename T> A(T) -> A<T>;
+
+export module M;
+
+template <typename T>
+struct B {
+  template <typename U> B(U);
+};
+B(int) -> B<int>;
+
+// Accessing deduction guides should be possible,
+// even if we can't name the type directly.
+export A<void> f();
+export B<void> g();
diff --git a/gcc/testsuite/g++.dg/modules/dguide-2_b.C b/gcc/testsuite/g++.dg/modules/dguide-2_b.C
new file mode 100644 (file)
index 0000000..ca31306
--- /dev/null
@@ -0,0 +1,19 @@
+// PR c++/115231
+// { dg-additional-options "-fmodules-ts" }
+
+import M;
+
+template <typename>
+struct U;
+
+template <template <typename> typename TT, typename Inner>
+struct U<TT<Inner>> {
+  void go() {
+    TT t(10);
+  }
+};
+
+int main() {
+  U<decltype(f())>{}.go();
+  U<decltype(g())>{}.go();
+}
diff --git a/gcc/testsuite/g++.dg/modules/dguide-3_a.C b/gcc/testsuite/g++.dg/modules/dguide-3_a.C
new file mode 100644 (file)
index 0000000..33350ce
--- /dev/null
@@ -0,0 +1,10 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi A }
+
+export module A;
+
+extern "C++" {
+  template <typename T> struct S;
+  S(int) -> S<int>;
+  S(double) -> S<double>;
+}
diff --git a/gcc/testsuite/g++.dg/modules/dguide-3_b.C b/gcc/testsuite/g++.dg/modules/dguide-3_b.C
new file mode 100644 (file)
index 0000000..d23696c
--- /dev/null
@@ -0,0 +1,10 @@
+// { dg-additional-options "-fmodules-ts" }
+// { dg-module-cmi B }
+
+export module B;
+
+extern "C++" {
+  template <typename T> struct S;
+  S(int) -> S<int>;
+  S(char) -> S<char>;
+}
diff --git a/gcc/testsuite/g++.dg/modules/dguide-3_c.C b/gcc/testsuite/g++.dg/modules/dguide-3_c.C
new file mode 100644 (file)
index 0000000..95d29db
--- /dev/null
@@ -0,0 +1,6 @@
+// { dg-additional-options "-fmodules-ts -Wno-global-module" }
+
+module;
+template <typename T> struct S;
+export module C;
+S(const char*) -> S<const char*>;
diff --git a/gcc/testsuite/g++.dg/modules/dguide-3_d.C b/gcc/testsuite/g++.dg/modules/dguide-3_d.C
new file mode 100644 (file)
index 0000000..0d98517
--- /dev/null
@@ -0,0 +1,30 @@
+// { dg-additional-options "-fmodules-ts" }
+// Test merging deduction guides.
+
+template <typename T> struct S {
+  template <typename U> S(U);
+};
+
+import A;
+import B;
+import C;
+
+int main() {
+  // declared in A and B
+  S x(123);
+  S<int> x2 = x;
+
+  // declared only in A
+  S y(0.5);
+  S<double> y2 = y;
+
+  // declared only in B
+  S z('c');
+  S<char> z2 = z;
+
+  // declared only in C (and attached to named module)
+  S w("hello");
+  S<const char*> w2 = w;
+}
+
+S(char) -> S<double>;  // { dg-error "ambiguating" }