]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++/modules: Fix ICE on merge of instantiation with partial spec [PR120013]
authorNathaniel Shead <nathanieloshead@gmail.com>
Sat, 17 May 2025 13:51:07 +0000 (23:51 +1000)
committerNathaniel Shead <nathanieloshead@gmail.com>
Tue, 20 May 2025 02:09:44 +0000 (12:09 +1000)
When we import a pending instantiation that matches an existing partial
specialisation, we don't find the slot in the entity map because for
partial specialisations we register the TEMPLATE_DECL but for normal
implicit instantiations we instead register the inner TYPE_DECL.

Because the DECL_MODULE_ENTITY_P flag is set we correctly realise that
it is in the entity map, but ICE when attempting to use that slot in
partition handling.

This patch fixes the issue by detecting this case and instead looking
for the slot for the TEMPLATE_DECL.  It doesn't matter that we never add
a slot for the inner decl because we're about to discard it anyway.

PR c++/120013

gcc/cp/ChangeLog:

* module.cc (trees_in::install_entity): Handle re-registering
the inner TYPE_DECL of a partial specialisation.

gcc/testsuite/ChangeLog:

* g++.dg/modules/partial-8.h: New test.
* g++.dg/modules/partial-8_a.C: New test.
* g++.dg/modules/partial-8_b.C: New test.
* g++.dg/modules/partial-8_c.C: New test.
* g++.dg/modules/partial-8_d.C: New test.

Signed-off-by: Nathaniel Shead <nathanieloshead@gmail.com>
Reviewed-by: Jason Merrill <jason@redhat.com>
gcc/cp/module.cc
gcc/testsuite/g++.dg/modules/partial-8.h [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/partial-8_a.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/partial-8_b.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/partial-8_c.C [new file with mode: 0644]
gcc/testsuite/g++.dg/modules/partial-8_d.C [new file with mode: 0644]

index 200e1c2deb36f803f720dcd30074434072568029..f728275612eb6607e912df7d06a967734a9e8525 100644 (file)
@@ -8083,18 +8083,37 @@ trees_in::install_entity (tree decl)
       gcc_checking_assert (!existed);
       slot = ident;
     }
-  else if (state->is_partition ())
-    {
-      /* The decl is already in the entity map, but we see it again now from a
-        partition: we want to overwrite if the original decl wasn't also from
-        a (possibly different) partition.  Otherwise, for things like template
-        instantiations, make_dependency might not realise that this is also
-        provided from a partition and should be considered part of this module
-        (and thus always emitted into the primary interface's CMI).  */
+  else
+    {
       unsigned *slot = entity_map->get (DECL_UID (decl));
-      module_state *imp = import_entity_module (*slot);
-      if (!imp->is_partition ())
-       *slot = ident;
+
+      /* The entity must be in the entity map already.  However, DECL may
+        be the DECL_TEMPLATE_RESULT of an existing partial specialisation
+        if we matched it while streaming another instantiation; in this
+        case we already registered that TEMPLATE_DECL.  */
+      if (!slot)
+       {
+         tree type = TREE_TYPE (decl);
+         gcc_checking_assert (TREE_CODE (decl) == TYPE_DECL
+                              && CLASS_TYPE_P (type)
+                              && CLASSTYPE_TEMPLATE_SPECIALIZATION (type));
+         slot = entity_map->get (DECL_UID (CLASSTYPE_TI_TEMPLATE (type)));
+       }
+      gcc_checking_assert (slot);
+
+      if (state->is_partition ())
+       {
+         /* The decl is already in the entity map, but we see it again now
+            from a partition: we want to overwrite if the original decl
+            wasn't also from a (possibly different) partition.  Otherwise,
+            for things like template instantiations, make_dependency might
+            not realise that this is also provided from a partition and
+            should be considered part of this module (and thus always
+            emitted into the primary interface's CMI).  */
+         module_state *imp = import_entity_module (*slot);
+         if (!imp->is_partition ())
+           *slot = ident;
+       }
     }
 
   return true;
diff --git a/gcc/testsuite/g++.dg/modules/partial-8.h b/gcc/testsuite/g++.dg/modules/partial-8.h
new file mode 100644 (file)
index 0000000..d9a83a8
--- /dev/null
@@ -0,0 +1,8 @@
+// PR c++/120013
+
+template <typename> struct tuple_element;
+template <typename T> tuple_element<T*> get(T);
+
+// This case wasn't an issue for the PR, but worth double-checking
+template <typename> constexpr int var = 123;
+template <typename T> void foo(T, int = var<T*>);
diff --git a/gcc/testsuite/g++.dg/modules/partial-8_a.C b/gcc/testsuite/g++.dg/modules/partial-8_a.C
new file mode 100644 (file)
index 0000000..d6848c7
--- /dev/null
@@ -0,0 +1,10 @@
+// PR c++/120013
+// { dg-additional-options "-fmodules -Wno-global-module" }
+// { dg-module-cmi m:a }
+
+module;
+#include "partial-8.h"
+template <typename T> struct tuple_element<T*>;
+template <typename T> constexpr int var<T*> = 456;
+module m:a;
+template <typename T> void a(T t) { ::get(t); foo(t); }
diff --git a/gcc/testsuite/g++.dg/modules/partial-8_b.C b/gcc/testsuite/g++.dg/modules/partial-8_b.C
new file mode 100644 (file)
index 0000000..ce5cd09
--- /dev/null
@@ -0,0 +1,8 @@
+// PR c++/120013
+// { dg-additional-options "-fmodules -Wno-global-module" }
+// { dg-module-cmi m:b }
+
+module;
+#include "partial-8.h"
+module m:b;
+template <typename T> void b(T t) { ::get(t); foo(t); }
diff --git a/gcc/testsuite/g++.dg/modules/partial-8_c.C b/gcc/testsuite/g++.dg/modules/partial-8_c.C
new file mode 100644 (file)
index 0000000..eadd282
--- /dev/null
@@ -0,0 +1,7 @@
+// PR c++/120013
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi m }
+
+export module m;
+import :a;
+import :b;
diff --git a/gcc/testsuite/g++.dg/modules/partial-8_d.C b/gcc/testsuite/g++.dg/modules/partial-8_d.C
new file mode 100644 (file)
index 0000000..2aedb39
--- /dev/null
@@ -0,0 +1,9 @@
+// PR c++/120013
+// { dg-additional-options "-fmodules" }
+// { dg-module-cmi m }
+// Same as partial-8_c.C but in the other order, to ensure
+// that loading a partial spec over an instantiation works
+
+export module m;
+import :b;
+import :a;