[Wasm GC] Do not cache signature types in nominal mode if they have a super (#5364)

This reduces the amount of public types, since if there is a super then using the
type in a public place would make the super also public. It is safer for closed-world
mode to reuse types without supers.
diff --git a/src/wasm/wasm-type.cpp b/src/wasm/wasm-type.cpp
index 284d6d7..b65debc 100644
--- a/src/wasm/wasm-type.cpp
+++ b/src/wasm/wasm-type.cpp
@@ -2967,7 +2967,11 @@
 
   // Note built signature types. See comment in `HeapType::HeapType(Signature)`.
   for (auto type : state.results) {
-    if (type.isSignature() && (getTypeSystem() == TypeSystem::Nominal)) {
+    // Do not cache types with explicit supertypes (that is, whose supertype is
+    // not HeapType::func). We don't want to reuse such types because then we'd
+    // be adding subtyping relationships that are not in the input.
+    if (type.isSignature() && (getTypeSystem() == TypeSystem::Nominal) &&
+        !type.getSuperType()) {
       nominalSignatureCache.insertType(type);
     }
   }
diff --git a/test/lit/parse-nominal-types-no-sig-sharing.wast b/test/lit/parse-nominal-types-no-sig-sharing.wast
new file mode 100644
index 0000000..f3ebecc
--- /dev/null
+++ b/test/lit/parse-nominal-types-no-sig-sharing.wast
@@ -0,0 +1,41 @@
+;; NOTE: Assertions have been generated by update_lit_checks.py --all-items and should not be edited.
+
+;; RUN: foreach %s %t wasm-opt --nominal -all                -S -o - | filecheck %s
+;; RUN: foreach %s %t wasm-opt --nominal -all --roundtrip    -S -o - | filecheck %s
+;; RUN: foreach %s %t wasm-opt --nominal -all --closed-world -S -o - | filecheck %s
+
+;; Test that we do not use the signature cache to share a function type that
+;; has a supertype. $sub appears first, so it is the earliest example of a
+;; signature of no params and no results, but we should not use it as the types
+;; of $foo or $bar, as it has a supertype. Instead, we should use $super as the
+;; canonical type for that signature. That is fine as at least it only makes
+;; $super effectively a public type, instead of both $sub and $super (the latter
+;; does not validate in closed world, but the former does - the closed-world
+;; run that we do here would error).
+;;
+;; Note that this problem only happens in nominal mode, since $sub can appear
+;; before $super in the list of types.
+
+(module
+  (type $sub (func_subtype $super))
+
+  ;; CHECK:      (type $super (func))
+  (type $super (func_subtype func))
+
+  ;; CHECK:      (global $g (ref null $super) (ref.null nofunc))
+  (global $g (ref null $super) (ref.null $sub))
+
+  ;; CHECK:      (export "foo" (func $foo))
+
+  ;; CHECK:      (func $foo (type $super)
+  ;; CHECK-NEXT:  (nop)
+  ;; CHECK-NEXT: )
+  (func $foo (export "foo")
+  )
+
+  ;; CHECK:      (func $bar (type $super)
+  ;; CHECK-NEXT:  (nop)
+  ;; CHECK-NEXT: )
+  (func $bar
+  )
+)