Repository: bytecodealliance/wasmtime
Pull request #8467 (2024-04-24 21:02:15)

cranelift/x64: Allow collecting Gpr/Xmm operands
Previously, the operand collector required its arguments to be of type Reg. But the x86-64 backend uses newtype wrappers around Reg to ensure that different register classes don't get mixed up, so it had to convert types everywhere to pass registers to the operand collector.

This commit allows the operand collector to accept any type which implements `Into<Reg>` instead. The newtype wrappers already implemented that, so we can mostly just delete the `to_reg` and `to_writable_reg` calls.

However, those calls were also automatically dereferencing borrows as needed, so to minimize the need for explicit dereferences I've also introduced implementations for converting from &Gpr/&Xmm to Reg. That doesn't help with the Writable-wrapped registers but it's good enough. I have a PR in progress which will clean it up the rest of the way.


Patch:
diff --git a/cranelift/codegen/src/isa/x64/inst/args.rs b/cranelift/codegen/src/isa/x64/inst/args.rs
index a434703bb..aa7f19ce5 100644
--- a/cranelift/codegen/src/isa/x64/inst/args.rs
+++ b/cranelift/codegen/src/isa/x64/inst/args.rs
@@ -55,6 +55,12 @@ macro_rules! newtype_of_reg {
             }
         }
 
+        impl From<&$newtype_reg> for Reg {
+            fn from(r: &$newtype_reg) -> Self {
+                r.0
+            }
+        }
+
         impl $newtype_reg {
             /// Create this newtype from the given register, or return `None` if the register
             /// is not a valid instance of this newtype.
@@ -365,13 +371,13 @@ impl Amode {
                     collector.reg_use(*base);
                 }
             }
-            Amode::ImmRegRegShift { base, index, .. } => {
+            &Amode::ImmRegRegShift { base, index, .. } => {
                 debug_assert_ne!(base.to_reg(), regs::rbp());
                 debug_assert_ne!(base.to_reg(), regs::rsp());
-                collector.reg_use(base.to_reg());
+                collector.reg_use(base);
                 debug_assert_ne!(index.to_reg(), regs::rbp());
                 debug_assert_ne!(index.to_reg(), regs::rsp());
-                collector.reg_use(index.to_reg());
+                collector.reg_use(index);
             }
             Amode::RipRelative { .. } => {
                 // RIP isn't involved in regalloc.
@@ -388,9 +394,9 @@ impl Amode {
             Amode::ImmReg { base, .. } => {
                 collector.reg_late_use(*base);
             }
-            Amode::ImmRegRegShift { base, index, .. } => {
-                collector.reg_late_use(base.to_reg());
-                collector.reg_late_use(index.to_reg());
+            &Amode::ImmRegRegShift { base, index, .. } => {
+                collector.reg_late_use(base);
+                collector.reg_late_use(index);
             }
             Amode::RipRelative { .. } => {
                 // RIP isn't involved in regalloc.
diff --git a/cranelift/codegen/src/isa/x64/inst/mod.rs b/cranelift/codegen/src/isa/x64/inst/mod.rs
index 5785a68c3..277bafd8e 100644
--- a/cranelift/codegen/src/isa/x64/inst/mod.rs
+++ b/cranelift/codegen/src/isa/x64/inst/mod.rs
@@ -1912,29 +1912,29 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         Inst::AluRmiR {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
-        Inst::AluConstOp { dst, .. } => collector.reg_def(dst.to_writable_reg()),
+        &Inst::AluConstOp { dst, .. } => collector.reg_def(dst),
         Inst::AluRM { src1_dst, src2, .. } => {
-            collector.reg_use(src2.to_reg());
+            collector.reg_use(src2);
             src1_dst.get_operands(collector);
         }
         Inst::AluRmRVex {
             src1, src2, dst, ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
-        Inst::Not { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+        &Inst::Not { src, dst, .. } => {
+            collector.reg_use(src);
+            collector.reg_reuse_def(dst, 0);
         }
-        Inst::Neg { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+        &Inst::Neg { src, dst, .. } => {
+            collector.reg_use(src);
+            collector.reg_reuse_def(dst, 0);
         }
         Inst::Div {
             dividend_lo,
@@ -1952,22 +1952,22 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         } => {
             match inst {
                 Inst::Div { divisor, .. } => divisor.get_operands(collector),
-                Inst::CheckedSRemSeq { divisor, .. } => collector.reg_use(divisor.to_reg()),
+                Inst::CheckedSRemSeq { divisor, .. } => collector.reg_use(divisor),
                 _ => {}
             }
-            collector.reg_fixed_use(dividend_lo.to_reg(), regs::rax());
-            collector.reg_fixed_use(dividend_hi.to_reg(), regs::rdx());
-            collector.reg_fixed_def(dst_quotient.to_writable_reg(), regs::rax());
-            collector.reg_fixed_def(dst_remainder.to_writable_reg(), regs::rdx());
+            collector.reg_fixed_use(dividend_lo, regs::rax());
+            collector.reg_fixed_use(dividend_hi, regs::rdx());
+            collector.reg_fixed_def(*dst_quotient, regs::rax());
+            collector.reg_fixed_def(*dst_remainder, regs::rdx());
         }
         Inst::Div8 { dividend, dst, .. } | Inst::CheckedSRemSeq8 { dividend, dst, .. } => {
             match inst {
                 Inst::Div8 { divisor, .. } => divisor.get_operands(collector),
-                Inst::CheckedSRemSeq8 { divisor, .. } => collector.reg_use(divisor.to_reg()),
+                Inst::CheckedSRemSeq8 { divisor, .. } => collector.reg_use(divisor),
                 _ => {}
             }
-            collector.reg_fixed_use(dividend.to_reg(), regs::rax());
-            collector.reg_fixed_def(dst.to_writable_reg(), regs::rax());
+            collector.reg_fixed_use(dividend, regs::rax());
+            collector.reg_fixed_def(*dst, regs::rax());
         }
         Inst::Mul {
             src1,
@@ -1976,53 +1976,53 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst_hi,
             ..
         } => {
-            collector.reg_fixed_use(src1.to_reg(), regs::rax());
-            collector.reg_fixed_def(dst_lo.to_writable_reg(), regs::rax());
-            collector.reg_fixed_def(dst_hi.to_writable_reg(), regs::rdx());
+            collector.reg_fixed_use(src1, regs::rax());
+            collector.reg_fixed_def(*dst_lo, regs::rax());
+            collector.reg_fixed_def(*dst_hi, regs::rdx());
             src2.get_operands(collector);
         }
         Inst::Mul8 {
             src1, src2, dst, ..
         } => {
-            collector.reg_fixed_use(src1.to_reg(), regs::rax());
-            collector.reg_fixed_def(dst.to_writable_reg(), regs::rax());
+            collector.reg_fixed_use(src1, regs::rax());
+            collector.reg_fixed_def(*dst, regs::rax());
             src2.get_operands(collector);
         }
         Inst::IMul {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
         Inst::IMulImm { src1, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src1.get_operands(collector);
         }
-        Inst::SignExtendData { size, src, dst } => {
+        &Inst::SignExtendData { size, src, dst } => {
             match size {
                 OperandSize::Size8 => {
                     // Note `rax` on both src and dest: 8->16 extend
                     // does AL -> AX.
-                    collector.reg_fixed_use(src.to_reg(), regs::rax());
-                    collector.reg_fixed_def(dst.to_writable_reg(), regs::rax());
+                    collector.reg_fixed_use(src, regs::rax());
+                    collector.reg_fixed_def(dst, regs::rax());
                 }
                 _ => {
                     // All other widths do RAX -> RDX (AX -> DX:AX,
                     // EAX -> EDX:EAX).
-                    collector.reg_fixed_use(src.to_reg(), regs::rax());
-                    collector.reg_fixed_def(dst.to_writable_reg(), regs::rdx());
+                    collector.reg_fixed_use(src, regs::rax());
+                    collector.reg_fixed_def(dst, regs::rdx());
                 }
             }
         }
         Inst::UnaryRmR { src, dst, .. }
         | Inst::UnaryRmRVex { src, dst, .. }
         | Inst::UnaryRmRImmVex { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::XmmUnaryRmR { src, dst, .. } | Inst::XmmUnaryRmRImm { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::XmmUnaryRmREvex { src, dst, .. }
@@ -2030,21 +2030,21 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         | Inst::XmmUnaryRmRUnaligned { src, dst, .. }
         | Inst::XmmUnaryRmRVex { src, dst, .. }
         | Inst::XmmUnaryRmRImmVex { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::XmmRmR {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
         Inst::XmmRmRUnaligned {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
         Inst::XmmRmRBlend {
@@ -2054,35 +2054,34 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst,
             op,
         } => {
-            assert!(
-                *op == SseOpcode::Blendvpd
-                    || *op == SseOpcode::Blendvps
-                    || *op == SseOpcode::Pblendvb
-            );
-            collector.reg_use(src1.to_reg());
-            collector.reg_fixed_use(mask.to_reg(), regs::xmm0());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            assert!(matches!(
+                op,
+                SseOpcode::Blendvpd | SseOpcode::Blendvps | SseOpcode::Pblendvb
+            ));
+            collector.reg_use(src1);
+            collector.reg_fixed_use(mask, regs::xmm0());
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
         Inst::XmmRmiRVex {
             src1, src2, dst, ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
         Inst::XmmRmRImmVex {
             src1, src2, dst, ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
         Inst::XmmVexPinsr {
             src1, src2, dst, ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
         Inst::XmmRmRVex3 {
@@ -2092,9 +2091,9 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst,
             ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
-            collector.reg_use(src2.to_reg());
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
+            collector.reg_use(src2);
             src3.get_operands(collector);
         }
         Inst::XmmRmRBlendVex {
@@ -2104,10 +2103,10 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst,
             ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
-            collector.reg_use(mask.to_reg());
+            collector.reg_use(mask);
         }
         Inst::XmmRmREvex {
             op,
@@ -2117,9 +2116,9 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             ..
         } => {
             assert_ne!(*op, Avx512Opcode::Vpermi2b);
-            collector.reg_use(src1.to_reg());
+            collector.reg_use(src1);
             src2.get_operands(collector);
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
         }
         Inst::XmmRmREvex3 {
             op,
@@ -2130,10 +2129,10 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             ..
         } => {
             assert_eq!(*op, Avx512Opcode::Vpermi2b);
-            collector.reg_use(src1.to_reg());
-            collector.reg_use(src2.to_reg());
+            collector.reg_use(src1);
+            collector.reg_use(src2);
             src3.get_operands(collector);
-            collector.reg_reuse_def(dst.to_writable_reg(), 0); // Reuse `src1`.
+            collector.reg_reuse_def(*dst, 0); // Reuse `src1`.
         }
         Inst::XmmRmRImm {
             src1, src2, dst, ..
@@ -2142,101 +2141,101 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
-        Inst::XmmUninitializedValue { dst } => collector.reg_def(dst.to_writable_reg()),
-        Inst::XmmMinMaxSeq { lhs, rhs, dst, .. } => {
-            collector.reg_use(rhs.to_reg());
-            collector.reg_use(lhs.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0); // Reuse RHS.
+        &Inst::XmmUninitializedValue { dst } => collector.reg_def(dst),
+        &Inst::XmmMinMaxSeq { lhs, rhs, dst, .. } => {
+            collector.reg_use(rhs);
+            collector.reg_use(lhs);
+            collector.reg_reuse_def(dst, 0); // Reuse RHS.
         }
         Inst::XmmRmiReg {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0); // Reuse RHS.
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0); // Reuse RHS.
             src2.get_operands(collector);
         }
         Inst::XmmMovRM { src, dst, .. }
         | Inst::XmmMovRMVex { src, dst, .. }
         | Inst::XmmMovRMImm { src, dst, .. }
         | Inst::XmmMovRMImmVex { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
+            collector.reg_use(src);
             dst.get_operands(collector);
         }
         Inst::XmmCmpRmR { src1, src2, .. } => {
-            collector.reg_use(src1.to_reg());
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
         Inst::XmmCmpRmRVex { src1, src2, .. } => {
-            collector.reg_use(src1.to_reg());
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
-        Inst::Imm { dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+        &Inst::Imm { dst, .. } => {
+            collector.reg_def(dst);
         }
-        Inst::MovRR { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_def(dst.to_writable_reg());
+        &Inst::MovRR { src, dst, .. } => {
+            collector.reg_use(src);
+            collector.reg_def(dst);
         }
-        Inst::MovFromPReg { dst, src } => {
+        &Inst::MovFromPReg { dst, src } => {
             debug_assert!(dst.to_reg().to_reg().is_virtual());
-            collector.reg_fixed_nonallocatable(*src);
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_fixed_nonallocatable(src);
+            collector.reg_def(dst);
         }
         Inst::MovToPReg { dst, src } => {
             debug_assert!(src.to_reg().is_virtual());
-            collector.reg_use(src.to_reg());
+            collector.reg_use(src);
             collector.reg_fixed_nonallocatable(*dst);
         }
-        Inst::XmmToGpr { src, dst, .. }
-        | Inst::XmmToGprVex { src, dst, .. }
-        | Inst::XmmToGprImm { src, dst, .. }
-        | Inst::XmmToGprImmVex { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_def(dst.to_writable_reg());
+        &Inst::XmmToGpr { src, dst, .. }
+        | &Inst::XmmToGprVex { src, dst, .. }
+        | &Inst::XmmToGprImm { src, dst, .. }
+        | &Inst::XmmToGprImmVex { src, dst, .. } => {
+            collector.reg_use(src);
+            collector.reg_def(dst);
         }
         Inst::GprToXmm { src, dst, .. } | Inst::GprToXmmVex { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::CvtIntToFloat {
             src1, src2, dst, ..
         } => {
-            collector.reg_use(src1.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src1);
+            collector.reg_reuse_def(*dst, 0);
             src2.get_operands(collector);
         }
         Inst::CvtIntToFloatVex {
             src1, src2, dst, ..
         } => {
-            collector.reg_def(dst.to_writable_reg());
-            collector.reg_use(src1.to_reg());
+            collector.reg_def(*dst);
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
-        Inst::CvtUint64ToFloatSeq {
+        &Inst::CvtUint64ToFloatSeq {
             src,
             dst,
             tmp_gpr1,
             tmp_gpr2,
             ..
         } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_early_def(dst.to_writable_reg());
-            collector.reg_early_def(tmp_gpr1.to_writable_reg());
-            collector.reg_early_def(tmp_gpr2.to_writable_reg());
+            collector.reg_use(src);
+            collector.reg_early_def(dst);
+            collector.reg_early_def(tmp_gpr1);
+            collector.reg_early_def(tmp_gpr2);
         }
-        Inst::CvtFloatToSintSeq {
+        &Inst::CvtFloatToSintSeq {
             src,
             dst,
             tmp_xmm,
             tmp_gpr,
             ..
         } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_early_def(dst.to_writable_reg());
-            collector.reg_early_def(tmp_gpr.to_writable_reg());
-            collector.reg_early_def(tmp_xmm.to_writable_reg());
+            collector.reg_use(src);
+            collector.reg_early_def(dst);
+            collector.reg_early_def(tmp_gpr);
+            collector.reg_early_def(tmp_xmm);
         }
-        Inst::CvtFloatToUintSeq {
+        &Inst::CvtFloatToUintSeq {
             src,
             dst,
             tmp_gpr,
@@ -2244,11 +2243,11 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             tmp_xmm2,
             ..
         } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_early_def(dst.to_writable_reg());
-            collector.reg_early_def(tmp_gpr.to_writable_reg());
-            collector.reg_early_def(tmp_xmm.to_writable_reg());
-            collector.reg_early_def(tmp_xmm2.to_writable_reg());
+            collector.reg_use(src);
+            collector.reg_early_def(dst);
+            collector.reg_early_def(tmp_gpr);
+            collector.reg_early_def(tmp_xmm);
+            collector.reg_early_def(tmp_xmm2);
         }
 
         Inst::MovImmM { dst, .. } => {
@@ -2256,44 +2255,44 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
         }
 
         Inst::MovzxRmR { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::Mov64MR { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::LoadEffectiveAddress { addr: src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::MovsxRmR { src, dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
             src.get_operands(collector);
         }
         Inst::MovRM { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
+            collector.reg_use(src);
             dst.get_operands(collector);
         }
         Inst::ShiftR {
             num_bits, src, dst, ..
         } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src);
+            collector.reg_reuse_def(*dst, 0);
             if let &Imm8Reg::Reg { reg } = num_bits.as_imm8_reg() {
                 collector.reg_fixed_use(reg, regs::rcx());
             }
         }
         Inst::CmpRmiR { src1, src2, .. } => {
-            collector.reg_use(src1.to_reg());
+            collector.reg_use(src1);
             src2.get_operands(collector);
         }
         Inst::Setcc { dst, .. } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
         }
         Inst::Bswap { src, dst, .. } => {
-            collector.reg_use(src.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(src);
+            collector.reg_reuse_def(*dst, 0);
         }
         Inst::Cmove {
             consequent,
@@ -2301,8 +2300,8 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst,
             ..
         } => {
-            collector.reg_use(alternative.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
+            collector.reg_use(alternative);
+            collector.reg_reuse_def(*dst, 0);
             consequent.get_operands(collector);
         }
         Inst::XmmCmove {
@@ -2311,15 +2310,15 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             dst,
             ..
         } => {
-            collector.reg_use(alternative.to_reg());
-            collector.reg_reuse_def(dst.to_writable_reg(), 0);
-            collector.reg_use(consequent.to_reg());
+            collector.reg_use(alternative);
+            collector.reg_reuse_def(*dst, 0);
+            collector.reg_use(consequent);
         }
         Inst::Push64 { src } => {
             src.get_operands(collector);
         }
         Inst::Pop64 { dst } => {
-            collector.reg_def(dst.to_writable_reg());
+            collector.reg_def(*dst);
         }
         Inst::StackProbeLoop { tmp, .. } => {
             collector.reg_early_def(*tmp);
@@ -2363,7 +2362,7 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
 
         Inst::ReturnCallKnown { callee, info } => {
             let ReturnCallInfo { uses, tmp, .. } = &**info;
-            collector.reg_fixed_def(tmp.to_writable_reg(), regs::r11());
+            collector.reg_fixed_def(*tmp, regs::r11());
             // Same as in the `Inst::CallKnown` branch.
             debug_assert_ne!(*callee, ExternalName::LibCall(LibCall::Probestack));
             for u in uses {
@@ -2381,24 +2380,21 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             // safe to use.
             collector.reg_fixed_use(*callee, regs::r10());
 
-            collector.reg_fixed_def(tmp.to_writable_reg(), regs::r11());
+            collector.reg_fixed_def(*tmp, regs::r11());
             for u in uses {
                 collector.reg_fixed_use(u.vreg, u.preg);
             }
         }
 
-        Inst::JmpTableSeq {
-            ref idx,
-            ref tmp1,
-            ref tmp2,
-            ..
+        &Inst::JmpTableSeq {
+            idx, tmp1, tmp2, ..
         } => {
-            collector.reg_use(*idx);
-            collector.reg_early_def(*tmp1);
+            collector.reg_use(idx);
+            collector.reg_early_def(tmp1);
             // In the sequence emitted for this pseudoinstruction in emit.rs,
             // tmp2 is only written after idx is read, so it doesn't need to be
             // an early def.
-            collector.reg_def(*tmp2);
+            collector.reg_def(tmp2);
         }
 
         Inst::JmpUnknown { target } => {
@@ -2466,8 +2462,8 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             // No registers are used.
         }
 
-        Inst::ElfTlsGetAddr { dst, .. } | Inst::MachOTlsGetAddr { dst, .. } => {
-            collector.reg_fixed_def(dst.to_writable_reg(), regs::rax());
+        &Inst::ElfTlsGetAddr { dst, .. } | &Inst::MachOTlsGetAddr { dst, .. } => {
+            collector.reg_fixed_def(dst, regs::rax());
             // All caller-saves are clobbered.
             //
             // We use the SysV calling convention here because the
@@ -2479,15 +2475,15 @@ fn x64_get_operands<F: Fn(VReg) -> VReg>(inst: &Inst, collector: &mut OperandCol
             collector.reg_clobbers(clobbers);
         }
 
-        Inst::CoffTlsGetAddr { dst, tmp, .. } => {
+        &Inst::CoffTlsGetAddr { dst, tmp, .. } => {
             // We also use the gs register. But that register is not allocatable by the
             // register allocator, so we don't need to mark it as used here.
 
             // We use %rax to set the address
-            collector.reg_fixed_def(dst.to_writable_reg(), regs::rax());
+            collector.reg_fixed_def(dst, regs::rax());
 
             // We use %rcx as a temporary variable to load the _tls_index
-            collector.reg_fixed_def(tmp.to_writable_reg(), regs::rcx());
+            collector.reg_fixed_def(tmp, regs::rcx());
         }
 
         Inst::Unwind { .. } => {}
diff --git a/cranelift/codegen/src/machinst/reg.rs b/cranelift/codegen/src/machinst/reg.rs
index 1407f80a4..4421a38f4 100644
--- a/cranelift/codegen/src/machinst/reg.rs
+++ b/cranelift/codegen/src/machinst/reg.rs
@@ -352,46 +352,47 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
 
     /// Add a register use, at the start of the instruction (`Before`
     /// position).
-    pub fn reg_use(&mut self, reg: Reg) {
-        self.reg_maybe_fixed(reg, OperandKind::Use, OperandPos::Early);
+    pub fn reg_use(&mut self, reg: impl Into<Reg>) {
+        self.reg_maybe_fixed(reg.into(), OperandKind::Use, OperandPos::Early);
     }
 
     /// Add a register use, at the end of the instruction (`After` position).
-    pub fn reg_late_use(&mut self, reg: Reg) {
-        self.reg_maybe_fixed(reg, OperandKind::Use, OperandPos::Late);
+    pub fn reg_late_use(&mut self, reg: impl Into<Reg>) {
+        self.reg_maybe_fixed(reg.into(), OperandKind::Use, OperandPos::Late);
     }
 
     /// Add a register def, at the end of the instruction (`After`
     /// position). Use only when this def will be written after all
     /// uses are read.
-    pub fn reg_def(&mut self, reg: Writable<Reg>) {
-        self.reg_maybe_fixed(reg.to_reg(), OperandKind::Def, OperandPos::Late);
+    pub fn reg_def(&mut self, reg: Writable<impl Into<Reg>>) {
+        self.reg_maybe_fixed(reg.to_reg().into(), OperandKind::Def, OperandPos::Late);
     }
 
     /// Add a register "early def", which logically occurs at the
     /// beginning of the instruction, alongside all uses. Use this
     /// when the def may be written before all uses are read; the
     /// regalloc will ensure that it does not overwrite any uses.
-    pub fn reg_early_def(&mut self, reg: Writable<Reg>) {
-        self.reg_maybe_fixed(reg.to_reg(), OperandKind::Def, OperandPos::Early);
+    pub fn reg_early_def(&mut self, reg: Writable<impl Into<Reg>>) {
+        self.reg_maybe_fixed(reg.to_reg().into(), OperandKind::Def, OperandPos::Early);
     }
 
     /// Add a register "fixed use", which ties a vreg to a particular
     /// RealReg at the end of the instruction.
-    pub fn reg_fixed_late_use(&mut self, reg: Reg, rreg: Reg) {
-        self.reg_fixed(reg, rreg, OperandKind::Use, OperandPos::Late);
+    pub fn reg_fixed_late_use(&mut self, reg: impl Into<Reg>, rreg: Reg) {
+        self.reg_fixed(reg.into(), rreg, OperandKind::Use, OperandPos::Late);
     }
 
     /// Add a register "fixed use", which ties a vreg to a particular
     /// RealReg at this point.
-    pub fn reg_fixed_use(&mut self, reg: Reg, rreg: Reg) {
-        self.reg_fixed(reg, rreg, OperandKind::Use, OperandPos::Early);
+    pub fn reg_fixed_use(&mut self, reg: impl Into<Reg>, rreg: Reg) {
+        self.reg_fixed(reg.into(), rreg, OperandKind::Use, OperandPos::Early);
     }
 
     /// Add a register "fixed def", which ties a vreg to a particular
     /// RealReg at this point.
-    pub fn reg_fixed_def(&mut self, reg: Writable<Reg>, rreg: Reg) {
-        self.reg_fixed(reg.to_reg(), rreg, OperandKind::Def, OperandPos::Late);
+    pub fn reg_fixed_def(&mut self, reg: Writable<impl Into<Reg>>, rreg: Reg) {
+        let reg = reg.to_reg().into();
+        self.reg_fixed(reg, rreg, OperandKind::Def, OperandPos::Late);
     }
 
     /// Add an operand tying a virtual register to a physical register.
@@ -419,20 +420,21 @@ impl<'a, F: Fn(VReg) -> VReg> OperandCollector<'a, F> {
     /// Add a register def that reuses an earlier use-operand's
     /// allocation. The index of that earlier operand (relative to the
     /// current instruction's start of operands) must be known.
-    pub fn reg_reuse_def(&mut self, reg: Writable<Reg>, idx: usize) {
-        if let Some(rreg) = reg.to_reg().to_real_reg() {
+    pub fn reg_reuse_def(&mut self, reg: Writable<impl Into<Reg>>, idx: usize) {
+        let reg = reg.to_reg().into();
+        if let Some(rreg) = reg.to_real_reg() {
             // In some cases we see real register arguments to a reg_reuse_def
             // constraint. We assume the creator knows what they're doing
             // here, though we do also require that the real register be a
             // fixed-nonallocatable register.
             self.reg_fixed_nonallocatable(rreg.into());
         } else {
-            debug_assert!(reg.to_reg().is_virtual());
+            debug_assert!(reg.is_virtual());
             // The operand we're reusing must not be fixed-nonallocatable, as
             // that would imply that the register has been allocated to a
             // virtual register.
             let constraint = OperandConstraint::Reuse(idx);
-            self.add_operand(reg.to_reg(), constraint, OperandKind::Def, OperandPos::Late);
+            self.add_operand(reg, constraint, OperandKind::Def, OperandPos::Late);
         }
     }
 
diff --git a/crates/c-api/src/table.rs b/crates/c-api/src/table.rs
index a3b8f1e36..404121c94 100644
--- a/crates/c-api/src/table.rs
+++ b/crates/c-api/src/table.rs
@@ -34,7 +34,7 @@ impl wasm_table_t {
 
 fn option_wasm_ref_t_to_ref(r: Option<&wasm_ref_t>, table_ty: &TableType) -> Ref {
     match (r.map(|r| r.r.clone()), table_ty.element().heap_type()) {
-        (None, HeapType::NoFunc | HeapType::Func | HeapType::Concrete(_)) => Ref::Func(None),
+        (None, HeapType::NoFunc | HeapType::Func | HeapType::ConcreteFunc(_)) => Ref::Func(None),
         (None, HeapType::Extern) => Ref::Extern(None),
         (None, HeapType::Any | HeapType::I31 | HeapType::None) => Ref::Any(None),
         (Some(r), _) => r,
diff --git a/crates/cranelift/src/compiler.rs b/crates/cranelift/src/compiler.rs
index 97f7b2410..a6c26c7f5 100644
--- a/crates/cranelift/src/compiler.rs
+++ b/crates/cranelift/src/compiler.rs
@@ -133,7 +133,7 @@ impl wasmtime_environ::Compiler for Compiler {
         let module = &translation.module;
         let func_index = module.func_index(func_index);
         let sig = translation.module.functions[func_index].signature;
-        let wasm_func_ty = &types[sig];
+        let wasm_func_ty = types[sig].unwrap_func();
 
         let mut compiler = self.function_compiler();
 
@@ -241,7 +241,7 @@ impl wasmtime_environ::Compiler for Compiler {
     ) -> Result<Box<dyn Any + Send>, CompileError> {
         let func_index = translation.module.func_index(def_func_index);
         let sig = translation.module.functions[func_index].signature;
-        let wasm_func_ty = &types[sig];
+        let wasm_func_ty = types[sig].unwrap_func();
 
         let isa = &*self.isa;
         let pointer_type = isa.pointer_type();
@@ -309,7 +309,7 @@ impl wasmtime_environ::Compiler for Compiler {
     ) -> Result<Box<dyn Any + Send>, CompileError> {
         let func_index = translation.module.func_index(def_func_index);
         let sig = translation.module.functions[func_index].signature;
-        let wasm_func_ty = &types[sig];
+        let wasm_func_ty = types[sig].unwrap_func();
 
         let isa = &*self.isa;
         let pointer_type = isa.pointer_type();
diff --git a/crates/cranelift/src/compiler/component.rs b/crates/cranelift/src/compiler/component.rs
index dc1736d14..250f7c304 100644
--- a/crates/cranelift/src/compiler/component.rs
+++ b/crates/cranelift/src/compiler/component.rs
@@ -43,7 +43,7 @@ impl<'a> TrampolineCompiler<'a> {
     ) -> TrampolineCompiler<'a> {
         let isa = &*compiler.isa;
         let signature = component.trampolines[index];
-        let ty = &types[signature];
+        let ty = types[signature].unwrap_func();
         let func = ir::Function::with_name_signature(
             ir::UserFuncName::user(0, 0),
             match abi {
@@ -127,7 +127,7 @@ impl<'a> TrampolineCompiler<'a> {
         let pointer_type = self.isa.pointer_type();
         let args = self.builder.func.dfg.block_params(self.block0).to_vec();
         let vmctx = args[0];
-        let wasm_func_ty = &self.types[self.signature];
+        let wasm_func_ty = self.types[self.signature].unwrap_func();
 
         // More handling is necessary here if this changes
         assert!(matches!(
@@ -291,7 +291,10 @@ impl<'a> TrampolineCompiler<'a> {
         host_args.push(args[2]);
 
         // Currently this only support resources represented by `i32`
-        assert_eq!(self.types[self.signature].params()[0], WasmValType::I32);
+        assert_eq!(
+            self.types[self.signature].unwrap_func().params()[0],
+            WasmValType::I32
+        );
         let (host_sig, offset) = host::resource_new32(self.isa, &mut self.builder.func);
 
         let host_fn = self.load_libcall(vmctx, offset);
@@ -322,7 +325,10 @@ impl<'a> TrampolineCompiler<'a> {
         host_args.push(args[2]);
 
         // Currently this only support resources represented by `i32`
-        assert_eq!(self.types[self.signature].returns()[0], WasmValType::I32);
+        assert_eq!(
+            self.types[self.signature].unwrap_func().returns()[0],
+            WasmValType::I32
+        );
         let (host_sig, offset) = host::resource_rep32(self.isa, &mut self.builder.func);
 
         let host_fn = self.load_libcall(vmctx, offset);
@@ -493,7 +499,7 @@ impl<'a> TrampolineCompiler<'a> {
 
             let sig = crate::wasm_call_signature(
                 self.isa,
-                &self.types[self.signature],
+                &self.types[self.signature].unwrap_func(),
                 &self.compiler.tunables,
             );
             let sig_ref = self.builder.import_signature(sig);
@@ -586,7 +592,7 @@ impl<'a> TrampolineCompiler<'a> {
             // and those are used to load the actual wasm parameters.
             Abi::Array => {
                 let results = self.compiler.load_values_from_array(
-                    self.types[self.signature].params(),
+                    self.types[self.signature].unwrap_func().params(),
                     &mut self.builder,
                     block0_params[2],
                     block0_params[3],
@@ -613,7 +619,7 @@ impl<'a> TrampolineCompiler<'a> {
                 let (ptr, len) = (block0_params[2], block0_params[3]);
                 self.compiler.store_values_to_array(
                     &mut self.builder,
-                    self.types[self.signature].returns(),
+                    self.types[self.signature].unwrap_func().returns(),
                     results,
                     ptr,
                     len,
diff --git a/crates/cranelift/src/func_environ.rs b/crates/cranelift/src/func_environ.rs
index a94d694f8..090af7f10 100644
--- a/crates/cranelift/src/func_environ.rs
+++ b/crates/cranelift/src/func_environ.rs
@@ -1180,7 +1180,7 @@ impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> {
             // Functions that have a statically known type are either going to
             // always succeed or always fail. Figure out by inspecting the types
             // further.
-            WasmHeapType::Concrete(EngineOrModuleTypeIndex::Module(table_ty)) => {
+            WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(table_ty)) => {
                 // If `ty_index` matches `table_ty`, then this call is
                 // statically known to have the right type, so no checks are
                 // necessary.
@@ -1223,8 +1223,8 @@ impl<'a, 'func, 'module_env> Call<'a, 'func, 'module_env> {
             // Engine-indexed types don't show up until runtime and it's a wasm
             // validation error to perform a call through a non-function table,
             // so these cases are dynamically not reachable.
-            WasmHeapType::Concrete(EngineOrModuleTypeIndex::Engine(_))
-            | WasmHeapType::Concrete(EngineOrModuleTypeIndex::RecGroup(_))
+            WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Engine(_))
+            | WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::RecGroup(_))
             | WasmHeapType::Extern
             | WasmHeapType::Any
             | WasmHeapType::I31
@@ -1419,7 +1419,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         init_value: ir::Value,
     ) -> WasmResult<ir::Value> {
         let grow = match self.module.table_plans[table_index].table.wasm_ty.heap_type {
-            WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+            WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                 self.builtin_functions.table_grow_func_ref(&mut pos.func)
             }
 
@@ -1479,7 +1479,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
             }
 
             // Function types.
-            WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+            WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                 match plan.style {
                     TableStyle::CallerChecksSignature => {
                         Ok(self.get_or_init_func_ref_table_elem(builder, table_index, index))
@@ -1532,7 +1532,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
             }
 
             // Function types.
-            WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+            WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                 match plan.style {
                     TableStyle::CallerChecksSignature => {
                         let (elem_addr, flags) = table_data.prepare_table_addr(
@@ -1566,7 +1566,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         len: ir::Value,
     ) -> WasmResult<()> {
         let libcall = match self.module.table_plans[table_index].table.wasm_ty.heap_type {
-            WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+            WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                 self.builtin_functions.table_fill_func_ref(&mut pos.func)
             }
             ty @ WasmHeapType::Extern
@@ -1625,7 +1625,7 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         ht: WasmHeapType,
     ) -> WasmResult<ir::Value> {
         Ok(match ht {
-            WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+            WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                 pos.ins().iconst(self.pointer_type(), 0)
             }
             WasmHeapType::Extern | WasmHeapType::Any | WasmHeapType::I31 | WasmHeapType::None => {
@@ -1983,7 +1983,8 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         index: TypeIndex,
     ) -> WasmResult<ir::SigRef> {
         let index = self.module.types[index];
-        let sig = crate::wasm_call_signature(self.isa, &self.types[index], &self.tunables);
+        let sig =
+            crate::wasm_call_signature(self.isa, self.types[index].unwrap_func(), &self.tunables);
         Ok(func.import_signature(sig))
     }
 
@@ -1993,7 +1994,8 @@ impl<'module_environment> cranelift_wasm::FuncEnvironment for FuncEnvironment<'m
         index: FuncIndex,
     ) -> WasmResult<ir::FuncRef> {
         let sig = self.module.functions[index].signature;
-        let sig = crate::wasm_call_signature(self.isa, &self.types[sig], &self.tunables);
+        let sig =
+            crate::wasm_call_signature(self.isa, self.types[sig].unwrap_func(), &self.tunables);
         let signature = func.import_signature(sig);
         let name =
             ir::ExternalName::User(func.declare_imported_user_function(ir::UserExternalName {
diff --git a/crates/cranelift/src/gc/enabled.rs b/crates/cranelift/src/gc/enabled.rs
index 43458814d..602a30e75 100644
--- a/crates/cranelift/src/gc/enabled.rs
+++ b/crates/cranelift/src/gc/enabled.rs
@@ -173,7 +173,7 @@ impl FuncEnvironment<'_> {
 
         let might_be_i31 = match ty.heap_type {
             WasmHeapType::Any => true,
-            WasmHeapType::Extern | WasmHeapType::None | WasmHeapType::Concrete(_) => false,
+            WasmHeapType::Extern | WasmHeapType::None | WasmHeapType::ConcreteFunc(_) => false,
             WasmHeapType::Func | WasmHeapType::NoFunc | WasmHeapType::I31 => {
                 unreachable!("we don't manage instances of these types with the GC")
             }
diff --git a/crates/cranelift/src/lib.rs b/crates/cranelift/src/lib.rs
index 41bcad224..fdf976891 100644
--- a/crates/cranelift/src/lib.rs
+++ b/crates/cranelift/src/lib.rs
@@ -267,7 +267,7 @@ fn wasm_call_signature(
 /// Returns the reference type to use for the provided wasm type.
 fn reference_type(wasm_ht: WasmHeapType, pointer_type: ir::Type) -> ir::Type {
     match wasm_ht {
-        WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => pointer_type,
+        WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => pointer_type,
         WasmHeapType::Extern | WasmHeapType::Any | WasmHeapType::I31 | WasmHeapType::None => {
             match pointer_type {
                 ir::types::I32 => ir::types::R32,
diff --git a/crates/environ/src/component/types.rs b/crates/environ/src/component/types.rs
index 93d002baf..2afd9d87c 100644
--- a/crates/environ/src/component/types.rs
+++ b/crates/environ/src/component/types.rs
@@ -480,21 +480,24 @@ impl ComponentTypesBuilder {
         (self.component_types, ty)
     }
 
-    /// Smaller helper method to find a `SignatureIndex` which corresponds to
-    /// the `resource.drop` intrinsic in components, namely a core wasm function
-    /// type which takes one `i32` argument and has no results.
+    /// Smaller helper method to find a `ModuleInternedTypeIndex` which
+    /// corresponds to the `resource.drop` intrinsic in components, namely a
+    /// core wasm function type which takes one `i32` argument and has no
+    /// results.
     ///
     /// This is a bit of a hack right now as ideally this find operation
-    /// wouldn't be needed and instead the `SignatureIndex` itself would be
-    /// threaded through appropriately, but that's left for a future
+    /// wouldn't be needed and instead the `ModuleInternedTypeIndex` itself
+    /// would be threaded through appropriately, but that's left for a future
     /// refactoring. Try not to lean too hard on this method though.
     pub fn find_resource_drop_signature(&self) -> Option<ModuleInternedTypeIndex> {
         self.module_types
-            .wasm_signatures()
-            .find(|(_, sig)| {
-                sig.params().len() == 1
-                    && sig.returns().len() == 0
-                    && sig.params()[0] == WasmValType::I32
+            .wasm_types()
+            .find(|(_, ty)| match &ty.composite_type {
+                wasmtime_types::WasmCompositeType::Func(sig) => {
+                    sig.params().len() == 1
+                        && sig.returns().len() == 0
+                        && sig.params()[0] == WasmValType::I32
+                }
             })
             .map(|(i, _)| i)
     }
diff --git a/crates/environ/src/module_environ.rs b/crates/environ/src/module_environ.rs
index 2c70a9fed..9d91ff570 100644
--- a/crates/environ/src/module_environ.rs
+++ b/crates/environ/src/module_environ.rs
@@ -550,7 +550,7 @@ impl<'a, 'data> ModuleEnvironment<'a, 'data> {
 
                 if self.tunables.generate_native_debuginfo {
                     let sig_index = self.result.module.functions[func_index].signature;
-                    let sig = &self.types[sig_index];
+                    let sig = self.types[sig_index].unwrap_func();
                     let mut locals = Vec::new();
                     for pair in body.get_locals_reader()? {
                         let (cnt, ty) = pair?;
@@ -1185,7 +1185,7 @@ impl ModuleTranslation<'_> {
                 .wasm_ty
                 .heap_type
             {
-                WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {}
+                WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {}
                 // If this is not a funcref table, then we can't support a
                 // pre-computed table of function indices. Technically this
                 // initializer won't trap so we could continue processing
diff --git a/crates/environ/src/module_types.rs b/crates/environ/src/module_types.rs
index 693175e8f..6005b7030 100644
--- a/crates/environ/src/module_types.rs
+++ b/crates/environ/src/module_types.rs
@@ -1,14 +1,14 @@
-use crate::{Module, PrimaryMap, TypeConvert, TypeIndex, WasmFuncType, WasmHeapType};
+use crate::{Module, PrimaryMap, TypeConvert, TypeIndex, WasmHeapType};
 use cranelift_entity::EntityRef;
 use serde_derive::{Deserialize, Serialize};
 use std::{
     collections::HashMap,
     ops::{Index, Range},
 };
-use wasmparser::{CompositeType, UnpackedIndex, Validator, ValidatorId};
+use wasmparser::{UnpackedIndex, Validator, ValidatorId};
 use wasmtime_types::{
-    wasm_unsupported, EngineOrModuleTypeIndex, ModuleInternedRecGroupIndex,
-    ModuleInternedTypeIndex, WasmResult,
+    EngineOrModuleTypeIndex, ModuleInternedRecGroupIndex, ModuleInternedTypeIndex, WasmResult,
+    WasmSubType,
 };
 
 /// All types used in a core wasm module.
@@ -21,7 +21,7 @@ use wasmtime_types::{
 #[derive(Default, Serialize, Deserialize)]
 pub struct ModuleTypes {
     rec_groups: PrimaryMap<ModuleInternedRecGroupIndex, Range<ModuleInternedTypeIndex>>,
-    wasm_types: PrimaryMap<ModuleInternedTypeIndex, WasmFuncType>,
+    wasm_types: PrimaryMap<ModuleInternedTypeIndex, WasmSubType>,
 }
 
 impl ModuleTypes {
@@ -29,12 +29,12 @@ impl ModuleTypes {
     /// this module.
     pub fn wasm_types(
         &self,
-    ) -> impl ExactSizeIterator<Item = (ModuleInternedTypeIndex, &WasmFuncType)> {
+    ) -> impl ExactSizeIterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
         self.wasm_types.iter()
     }
 
     /// Get the type at the specified index.
-    pub fn get(&self, ty: ModuleInternedTypeIndex) -> &WasmFuncType {
+    pub fn get(&self, ty: ModuleInternedTypeIndex) -> &WasmSubType {
         &self.wasm_types[ty]
     }
 
@@ -58,9 +58,9 @@ impl ModuleTypes {
 }
 
 impl Index<ModuleInternedTypeIndex> for ModuleTypes {
-    type Output = WasmFuncType;
+    type Output = WasmSubType;
 
-    fn index(&self, sig: ModuleInternedTypeIndex) -> &WasmFuncType {
+    fn index(&self, sig: ModuleInternedTypeIndex) -> &WasmSubType {
         &self.wasm_types[sig]
     }
 }
@@ -163,17 +163,8 @@ impl ModuleTypesBuilder {
 
         for id in validator_types.rec_group_elements(rec_group_id) {
             let ty = &validator_types[id];
-            if ty.supertype_idx.is_some() {
-                return Err(wasm_unsupported!("wasm gc: explicit subtyping"));
-            }
-            match &ty.composite_type {
-                CompositeType::Func(ty) => {
-                    let wasm_ty = WasmparserTypeConverter::new(self, module).convert_func_type(ty);
-                    self.wasm_func_type_in_rec_group(id, wasm_ty);
-                }
-                CompositeType::Array(_) => return Err(wasm_unsupported!("wasm gc: array types")),
-                CompositeType::Struct(_) => return Err(wasm_unsupported!("wasm gc: struct types")),
-            }
+            let wasm_ty = WasmparserTypeConverter::new(self, module).convert_sub_type(ty)?;
+            self.wasm_sub_type_in_rec_group(id, wasm_ty);
         }
 
         Ok(self.end_rec_group(rec_group_id))
@@ -275,18 +266,18 @@ impl ModuleTypesBuilder {
         Ok(interned_type)
     }
 
-    /// Define a new Wasm function type while we are defining a rec group.
-    fn wasm_func_type_in_rec_group(
+    /// Define a new Wasm type while we are defining a rec group.
+    fn wasm_sub_type_in_rec_group(
         &mut self,
         id: wasmparser::types::CoreTypeId,
-        func_ty: WasmFuncType,
+        ty: WasmSubType,
     ) -> ModuleInternedTypeIndex {
         assert!(
             self.defining_rec_group.is_some(),
             "must be defining a rec group to define new types"
         );
 
-        let module_interned_index = self.types.wasm_types.push(func_ty);
+        let module_interned_index = self.types.wasm_types.push(ty);
         debug_assert_eq!(
             self.wasmparser_to_wasmtime.get(&id),
             Some(&module_interned_index),
@@ -309,11 +300,9 @@ impl ModuleTypesBuilder {
         self.types.rec_group_elements(rec_group)
     }
 
-    /// Returns an iterator over all the wasm function signatures found within
-    /// this module.
-    pub fn wasm_signatures(
-        &self,
-    ) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmFuncType)> {
+    /// Returns an iterator over all the unique wasm types defined thus far
+    /// within this builder.
+    pub fn wasm_types(&self) -> impl Iterator<Item = (ModuleInternedTypeIndex, &WasmSubType)> {
         self.types.wasm_types()
     }
 }
@@ -348,12 +337,12 @@ impl TypeConvert for WasmparserTypeConverter<'_> {
         match index {
             UnpackedIndex::Id(id) => {
                 let signature = self.types.wasmparser_to_wasmtime[&id];
-                WasmHeapType::Concrete(EngineOrModuleTypeIndex::Module(signature))
+                WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(signature))
             }
             UnpackedIndex::Module(module_index) => {
                 let module_index = TypeIndex::from_u32(module_index);
                 let interned_index = self.module.types[module_index];
-                WasmHeapType::Concrete(EngineOrModuleTypeIndex::Module(interned_index))
+                WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(interned_index))
             }
             UnpackedIndex::RecGroup(_) => unreachable!(),
         }
diff --git a/crates/fuzzing/src/oracles/dummy.rs b/crates/fuzzing/src/oracles/dummy.rs
index da85f367c..0a504c0ad 100644
--- a/crates/fuzzing/src/oracles/dummy.rs
+++ b/crates/fuzzing/src/oracles/dummy.rs
@@ -48,7 +48,7 @@ pub fn dummy_value(val_ty: ValType) -> Result<Val> {
         ValType::Ref(r) => match r.heap_type() {
             _ if !r.is_nullable() => bail!("cannot construct a dummy value of type `{r}`"),
             HeapType::Extern => Val::null_extern_ref(),
-            HeapType::NoFunc | HeapType::Func | HeapType::Concrete(_) => Val::null_func_ref(),
+            HeapType::NoFunc | HeapType::Func | HeapType::ConcreteFunc(_) => Val::null_func_ref(),
             HeapType::Any | HeapType::I31 | HeapType::None => Val::null_any_ref(),
         },
     })
diff --git a/crates/runtime/src/instance.rs b/crates/runtime/src/instance.rs
index 13f75924e..8dc65abfc 100644
--- a/crates/runtime/src/instance.rs
+++ b/crates/runtime/src/instance.rs
@@ -897,8 +897,8 @@ impl Instance {
                                 VMGcRef::from_raw_u32(raw.get_anyref())
                             }),
                         )?,
-                    WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => table
-                        .init_func(
+                    WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
+                        table.init_func(
                             dst,
                             exprs.iter().map(|expr| unsafe {
                                 const_evaluator
@@ -907,7 +907,8 @@ impl Instance {
                                     .get_funcref()
                                     .cast()
                             }),
-                        )?,
+                        )?
+                    }
                 }
             }
         }
diff --git a/crates/runtime/src/instance/allocator.rs b/crates/runtime/src/instance/allocator.rs
index d1d2a2eb6..f33032ecf 100644
--- a/crates/runtime/src/instance/allocator.rs
+++ b/crates/runtime/src/instance/allocator.rs
@@ -594,7 +594,7 @@ fn initialize_tables(instance: &mut Instance, module: &Module) -> Result<()> {
                     }
 
                     wasmtime_environ::WasmHeapType::Func
-                    | wasmtime_environ::WasmHeapType::Concrete(_)
+                    | wasmtime_environ::WasmHeapType::ConcreteFunc(_)
                     | wasmtime_environ::WasmHeapType::NoFunc => {
                         let funcref = raw.get_funcref().cast::<VMFuncRef>();
                         let items = (0..table.size()).map(|_| funcref);
diff --git a/crates/runtime/src/table.rs b/crates/runtime/src/table.rs
index f138d1a37..484dfa022 100644
--- a/crates/runtime/src/table.rs
+++ b/crates/runtime/src/table.rs
@@ -248,7 +248,7 @@ impl From<DynamicGcRefTable> for Table {
 
 fn wasm_to_table_type(ty: WasmRefType) -> TableElementType {
     match ty.heap_type {
-        WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+        WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
             TableElementType::Func
         }
         WasmHeapType::Extern | WasmHeapType::Any | WasmHeapType::I31 | WasmHeapType::None => {
diff --git a/crates/runtime/src/vmcontext.rs b/crates/runtime/src/vmcontext.rs
index f4ba5a02a..840512fec 100644
--- a/crates/runtime/src/vmcontext.rs
+++ b/crates/runtime/src/vmcontext.rs
@@ -456,7 +456,7 @@ impl VMGlobalDefinition {
                 WasmHeapType::Any | WasmHeapType::I31 | WasmHeapType::None => {
                     global.init_gc_ref(VMGcRef::from_raw_u32(raw.get_anyref()))
                 }
-                WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+                WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                     *global.as_func_ref_mut() = raw.get_funcref().cast()
                 }
             },
@@ -485,7 +485,7 @@ impl VMGlobalDefinition {
                     self.as_gc_ref()
                         .map_or(0, |r| gc_store.clone_gc_ref(r).as_raw_u32()),
                 ),
-                WasmHeapType::Func | WasmHeapType::Concrete(_) | WasmHeapType::NoFunc => {
+                WasmHeapType::Func | WasmHeapType::ConcreteFunc(_) | WasmHeapType::NoFunc => {
                     ValRaw::funcref(self.as_func_ref().cast())
                 }
             },
diff --git a/crates/types/src/lib.rs b/crates/types/src/lib.rs
index 46a127a77..4a2c04ca3 100644
--- a/crates/types/src/lib.rs
+++ b/crates/types/src/lib.rs
@@ -376,7 +376,7 @@ impl EngineOrModuleTypeIndex {
 pub enum WasmHeapType {
     Extern,
     Func,
-    Concrete(EngineOrModuleTypeIndex),
+    ConcreteFunc(EngineOrModuleTypeIndex),
     NoFunc,
     Any,
     I31,
@@ -388,7 +388,7 @@ impl fmt::Display for WasmHeapType {
         match self {
             Self::Extern => write!(f, "extern"),
             Self::Func => write!(f, "func"),
-            Self::Concrete(i) => write!(f, "{i}"),
+            Self::ConcreteFunc(i) => write!(f, "func {i}"),
             Self::NoFunc => write!(f, "nofunc"),
             Self::Any => write!(f, "any"),
             Self::I31 => write!(f, "i31"),
@@ -403,7 +403,7 @@ impl TypeTrace for WasmHeapType {
         F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
     {
         match *self {
-            Self::Concrete(i) => func(i),
+            Self::ConcreteFunc(i) => func(i),
             _ => Ok(()),
         }
     }
@@ -413,7 +413,7 @@ impl TypeTrace for WasmHeapType {
         F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
     {
         match self {
-            Self::Concrete(i) => func(i),
+            Self::ConcreteFunc(i) => func(i),
             _ => Ok(()),
         }
     }
@@ -428,7 +428,7 @@ impl WasmHeapType {
             Self::Extern | Self::Any | Self::I31 | Self::None => true,
 
             // All `t <: (ref null func)` are not.
-            Self::Func | Self::Concrete(_) | Self::NoFunc => false,
+            Self::Func | Self::ConcreteFunc(_) | Self::NoFunc => false,
         }
     }
 
@@ -523,6 +523,95 @@ impl WasmFuncType {
     }
 }
 
+/// A function, array, or struct type.
+#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
+pub enum WasmCompositeType {
+    Func(WasmFuncType),
+    //
+    // TODO: Array and struct types.
+}
+
+impl WasmCompositeType {
+    #[inline]
+    pub fn is_func(&self) -> bool {
+        matches!(self, Self::Func(_))
+    }
+
+    #[inline]
+    pub fn as_func(&self) -> Option<&WasmFuncType> {
+        match self {
+            WasmCompositeType::Func(f) => Some(f),
+        }
+    }
+
+    #[inline]
+    pub fn unwrap_func(&self) -> &WasmFuncType {
+        self.as_func().unwrap()
+    }
+}
+
+impl TypeTrace for WasmCompositeType {
+    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
+    where
+        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
+    {
+        match self {
+            WasmCompositeType::Func(f) => f.trace(func),
+        }
+    }
+
+    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
+    where
+        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
+    {
+        match self {
+            WasmCompositeType::Func(f) => f.trace_mut(func),
+        }
+    }
+}
+
+/// A concrete, user-defined (or host-defined) Wasm type.
+#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
+pub struct WasmSubType {
+    // TODO: is_final, supertype
+    //
+    /// The array, function, or struct that is defined.
+    pub composite_type: WasmCompositeType,
+}
+
+impl WasmSubType {
+    #[inline]
+    pub fn is_func(&self) -> bool {
+        self.composite_type.is_func()
+    }
+
+    #[inline]
+    pub fn as_func(&self) -> Option<&WasmFuncType> {
+        self.composite_type.as_func()
+    }
+
+    #[inline]
+    pub fn unwrap_func(&self) -> &WasmFuncType {
+        self.composite_type.unwrap_func()
+    }
+}
+
+impl TypeTrace for WasmSubType {
+    fn trace<F, E>(&self, func: &mut F) -> Result<(), E>
+    where
+        F: FnMut(EngineOrModuleTypeIndex) -> Result<(), E>,
+    {
+        self.composite_type.trace(func)
+    }
+
+    fn trace_mut<F, E>(&mut self, func: &mut F) -> Result<(), E>
+    where
+        F: FnMut(&mut EngineOrModuleTypeIndex) -> Result<(), E>,
+    {
+        self.composite_type.trace_mut(func)
+    }
+}
+
 /// A recursive type group.
 ///
 /// Types within a recgroup can have forward references to each other, which
@@ -536,7 +625,7 @@ impl WasmFuncType {
 #[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
 pub struct WasmRecGroup {
     /// The types inside of this recgroup.
-    pub types: Box<[WasmFuncType]>,
+    pub types: Box<[WasmSubType]>,
 }
 
 impl TypeTrace for WasmRecGroup {
@@ -1062,6 +1151,27 @@ pub trait TypeConvert {
         }
     }
 
+    fn convert_sub_type(&self, ty: &wasmparser::SubType) -> WasmResult<WasmSubType> {
+        if ty.supertype_idx.is_some() {
+            return Err(wasm_unsupported!("wasm gc: explicit subtyping"));
+        }
+        let composite_type = self.convert_composite_type(&ty.composite_type)?;
+        Ok(WasmSubType { composite_type })
+    }
+
+    fn convert_composite_type(
+        &self,
+        ty: &wasmparser::CompositeType,
+    ) -> WasmResult<WasmCompositeType> {
+        match ty {
+            wasmparser::CompositeType::Func(f) => {
+                Ok(WasmCompositeType::Func(self.convert_func_type(f)))
+            }
+            wasmparser::CompositeType::Array(_) => Err(wasm_unsupported!("wasm gc: array types")),
+            wasmparser::CompositeType::Struct(_) => Err(wasm_unsupported!("wasm gc: struct types")),
+        }
+    }
+
     /// Converts a wasmparser function type to a wasmtime type
     fn convert_func_type(&self, ty: &wasmparser::FuncType) -> WasmFuncType {
         let params = ty
diff --git a/crates/wasmtime/src/compile.rs b/crates/wasmtime/src/compile.rs
index 5ea9e33bc..0727f6f8a 100644
--- a/crates/wasmtime/src/compile.rs
+++ b/crates/wasmtime/src/compile.rs
@@ -401,7 +401,8 @@ impl<'a> CompileInputs<'a> {
         if component.component.num_resources > 0 {
             if let Some(sig) = types.find_resource_drop_signature() {
                 ret.push_input(move |compiler| {
-                    let trampoline = compiler.compile_wasm_to_native_trampoline(&types[sig])?;
+                    let trampoline =
+                        compiler.compile_wasm_to_native_trampoline(types[sig].unwrap_func())?;
                     Ok(CompileOutput {
                         key: CompileKey::resource_drop_wasm_to_native_trampoline(),
                         symbol: "resource_drop_trampoline".to_string(),
@@ -493,7 +494,7 @@ impl<'a> CompileInputs<'a> {
 
         for signature in sigs {
             self.push_input(move |compiler| {
-                let wasm_func_ty = &types[signature];
+                let wasm_func_ty = types[signature].unwrap_func();
                 let trampoline = compiler.compile_wasm_to_native_trampoline(wasm_func_ty)?;
                 Ok(CompileOutput {
                     key: CompileKey::wasm_to_native_trampoline(signature),
diff --git a/crates/wasmtime/src/runtime/component/types.rs b/crates/wasmtime/src/runtime/component/types.rs
index b40780b5d..9536327ad 100644
--- a/crates/wasmtime/src/runtime/component/types.rs
+++ b/crates/wasmtime/src/runtime/component/types.rs
@@ -873,7 +873,7 @@ impl ComponentItem {
             TypeDef::Module(idx) => Self::Module(Module::from(*idx, ty)),
             TypeDef::CoreFunc(idx) => Self::CoreFunc(FuncType::from_wasm_func_type(
                 engine,
-                ty.types[*idx].clone(),
+                ty.types[*idx].unwrap_func().clone(),
             )),
             TypeDef::Resource(idx) => {
                 let resource_index = ty.types[*idx].ty;
diff --git a/crates/wasmtime/src/runtime/coredump.rs b/crates/wasmtime/src/runtime/coredump.rs
index 04b376185..ee98659fd 100644
--- a/crates/wasmtime/src/runtime/coredump.rs
+++ b/crates/wasmtime/src/runtime/coredump.rs
@@ -179,7 +179,7 @@ impl WasmCoreDump {
                     ValType::Ref(r) => match r.heap_type() {
                         HeapType::Extern => wasm_encoder::ValType::EXTERNREF,
 
-                        HeapType::Func | HeapType::Concrete(_) | HeapType::NoFunc => {
+                        HeapType::Func | HeapType::ConcreteFunc(_) | HeapType::NoFunc => {
                             wasm_encoder::ValType::FUNCREF
                         }
 
diff --git a/crates/wasmtime/src/runtime/externals/global.rs b/crates/wasmtime/src/runtime/externals/global.rs
index 5f78ca9ad..2af59fff7 100644
--- a/crates/wasmtime/src/runtime/externals/global.rs
+++ b/crates/wasmtime/src/runtime/externals/global.rs
@@ -117,7 +117,7 @@ impl Global {
                 ValType::V128 => Val::V128((*definition.as_u128()).into()),
                 ValType::Ref(ref_ty) => {
                     let reference: Ref = match ref_ty.heap_type() {
-                        HeapType::Func | HeapType::Concrete(_) => {
+                        HeapType::Func | HeapType::ConcreteFunc(_) => {
                             Func::_from_raw(&mut store, definition.as_func_ref().cast()).into()
                         }
 
diff --git a/crates/wasmtime/src/runtime/linker.rs b/crates/wasmtime/src/runtime/linker.rs
index 096a2a43e..3a93f9c3c 100644
--- a/crates/wasmtime/src/runtime/linker.rs
+++ b/crates/wasmtime/src/runtime/linker.rs
@@ -345,7 +345,7 @@ impl<T> Linker<T> {
                                         debug_assert!(r.is_nullable());
                                         match r.heap_type() {
                                             HeapType::Func
-                                            | HeapType::Concrete(_)
+                                            | HeapType::ConcreteFunc(_)
                                             | HeapType::NoFunc => Val::null_func_ref(),
                                             HeapType::Extern => Val::null_extern_ref(),
                                             HeapType::Any | HeapType::I31 | HeapType::None => {
diff --git a/crates/wasmtime/src/runtime/type_registry.rs b/crates/wasmtime/src/runtime/type_registry.rs
index 4262a40eb..729570961 100644
--- a/crates/wasmtime/src/runtime/type_registry.rs
+++ b/crates/wasmtime/src/runtime/type_registry.rs
@@ -20,7 +20,7 @@ use std::{
 };
 use wasmtime_environ::{
     iter_entity_range, EngineOrModuleTypeIndex, ModuleInternedTypeIndex, ModuleTypes, PrimaryMap,
-    TypeTrace, VMSharedTypeIndex, WasmFuncType, WasmRecGroup,
+    TypeTrace, VMSharedTypeIndex, WasmRecGroup, WasmSubType,
 };
 use wasmtime_slab::{Id as SlabId, Slab};
 
@@ -169,11 +169,11 @@ fn slab_id_to_shared_type_index(id: SlabId) -> VMSharedTypeIndex {
 /// Automatically unregisters the type on drop. (Unless other `RegisteredTypes`
 /// are keeping the type registered).
 ///
-/// Dereferences to its underlying `WasmFuncType`.
+/// Dereferences to its underlying `WasmSubType`.
 pub struct RegisteredType {
     engine: Engine,
     entry: RecGroupEntry,
-    ty: Arc<WasmFuncType>,
+    ty: Arc<WasmSubType>,
     index: VMSharedTypeIndex,
 }
 
@@ -218,7 +218,7 @@ impl Drop for RegisteredType {
 }
 
 impl std::ops::Deref for RegisteredType {
-    type Target = WasmFuncType;
+    type Target = WasmSubType;
 
     fn deref(&self) -> &Self::Target {
         &self.ty
@@ -254,7 +254,7 @@ impl Hash for RegisteredType {
 impl RegisteredType {
     /// Constructs a new `RegisteredType`, registering the given type with the
     /// engine's `TypeRegistry`.
-    pub fn new(engine: &Engine, ty: WasmFuncType) -> RegisteredType {
+    pub fn new(engine: &Engine, ty: WasmSubType) -> RegisteredType {
         let (entry, index, ty) = {
             log::trace!("RegisteredType::new({ty:?})");
 
@@ -317,7 +317,7 @@ impl RegisteredType {
         engine: Engine,
         entry: RecGroupEntry,
         index: VMSharedTypeIndex,
-        ty: Arc<WasmFuncType>,
+        ty: Arc<WasmSubType>,
     ) -> Self {
         debug_assert!(entry.0.registrations.load(Acquire) != 0);
         RegisteredType {
@@ -432,7 +432,7 @@ struct TypeRegistryInner {
     // Wasm type.
     //
     // These types are always canonicalized for runtime usage.
-    types: Slab<Arc<WasmFuncType>>,
+    types: Slab<Arc<WasmSubType>>,
 
     // A map that lets you walk backwards from a `VMSharedTypeIndex` to its
     // `RecGroupEntry`.
@@ -511,7 +511,7 @@ impl TypeRegistryInner {
         &mut self,
         map: &PrimaryMap<ModuleInternedTypeIndex, VMSharedTypeIndex>,
         range: Range<ModuleInternedTypeIndex>,
-        types: impl ExactSizeIterator<Item = WasmFuncType>,
+        types: impl ExactSizeIterator<Item = WasmSubType>,
     ) -> RecGroupEntry {
         debug_assert_eq!(iter_entity_range(range.clone()).len(), types.len());
 
@@ -594,7 +594,7 @@ impl TypeRegistryInner {
     }
 
     /// Is the given type canonicalized for runtime usage this registry?
-    fn assert_canonicalized_for_runtime_usage_in_this_registry(&self, ty: &WasmFuncType) {
+    fn assert_canonicalized_for_runtime_usage_in_this_registry(&self, ty: &WasmSubType) {
         ty.trace::<_, ()>(&mut |index| match index {
             EngineOrModuleTypeIndex::RecGroup(_) | EngineOrModuleTypeIndex::Module(_) => {
                 panic!("not canonicalized for runtime usage: {ty:?}")
@@ -619,7 +619,7 @@ impl TypeRegistryInner {
     fn insert_one_type_from_rec_group(
         &mut self,
         module_index: ModuleInternedTypeIndex,
-        ty: WasmFuncType,
+        ty: WasmSubType,
     ) -> VMSharedTypeIndex {
         // Despite being canonicalized for runtime usage, this type may still
         // have forward references to other types in the rec group we haven't
@@ -648,7 +648,7 @@ impl TypeRegistryInner {
     ///
     /// The returned entry will have already had its reference count incremented
     /// on behalf of callers.
-    fn register_singleton_rec_group(&mut self, ty: WasmFuncType) -> RecGroupEntry {
+    fn register_singleton_rec_group(&mut self, ty: WasmSubType) -> RecGroupEntry {
         self.assert_canonicalized_for_runtime_usage_in_this_registry(&ty);
 
         // This type doesn't have any module-level type references, since it is
@@ -800,7 +800,7 @@ impl TypeRegistry {
     /// still using the resulting value! Use the `RegisteredType::root`
     /// constructor if you need to ensure that property and you don't have some
     /// other mechanism already keeping the type registered.
-    pub fn borrow(&self, index: VMSharedTypeIndex) -> Option<Arc<WasmFuncType>> {
+    pub fn borrow(&self, index: VMSharedTypeIndex) -> Option<Arc<WasmSubType>> {
         let id = shared_type_index_to_slab_id(index);
         let inner = self.0.read().unwrap();
         inner.types.get(id).cloned()
diff --git a/crates/wasmtime/src/runtime/types.rs b/crates/wasmtime/src/runtime/types.rs
index 1cca5e5a9..1e306acbb 100644
--- a/crates/wasmtime/src/runtime/types.rs
+++ b/crates/wasmtime/src/runtime/types.rs
@@ -2,7 +2,8 @@ use anyhow::{bail, Result};
 use std::fmt::{self, Display};
 use wasmtime_environ::{
     EngineOrModuleTypeIndex, EntityType, Global, Memory, ModuleTypes, Table, TypeTrace,
-    VMSharedTypeIndex, WasmFuncType, WasmHeapType, WasmRefType, WasmValType,
+    VMSharedTypeIndex, WasmCompositeType, WasmFuncType, WasmHeapType, WasmRefType, WasmSubType,
+    WasmValType,
 };
 
 use crate::{type_registry::RegisteredType, Engine};
@@ -452,7 +453,7 @@ pub enum HeapType {
     /// specific, concrete type.
     ///
     /// This is a subtype of `func` and a supertype of `nofunc`.
-    Concrete(FuncType),
+    ConcreteFunc(FuncType),
 
     /// The `nofunc` heap type represents the null function reference.
     ///
@@ -486,7 +487,7 @@ impl Display for HeapType {
             HeapType::Any => write!(f, "any"),
             HeapType::I31 => write!(f, "i31"),
             HeapType::None => write!(f, "none"),
-            HeapType::Concrete(ty) => write!(f, "(concrete {:?})", ty.type_index()),
+            HeapType::ConcreteFunc(ty) => write!(f, "(concrete {:?})", ty.type_index()),
         }
     }
 }
@@ -494,7 +495,7 @@ impl Display for HeapType {
 impl From<FuncType> for HeapType {
     #[inline]
     fn from(f: FuncType) -> Self {
-        HeapType::Concrete(f)
+        HeapType::ConcreteFunc(f)
     }
 }
 
@@ -540,7 +541,7 @@ impl HeapType {
     ///
     /// Types that are not concrete, user-defined types are abstract types.
     pub fn is_concrete(&self) -> bool {
-        matches!(self, HeapType::Concrete(_))
+        matches!(self, HeapType::ConcreteFunc(_))
     }
 
     /// Get the underlying concrete, user-defined type, if any.
@@ -548,7 +549,7 @@ impl HeapType {
     /// Returns `None` for abstract types.
     pub fn as_concrete(&self) -> Option<&FuncType> {
         match self {
-            HeapType::Concrete(f) => Some(f),
+            HeapType::ConcreteFunc(f) => Some(f),
             _ => None,
         }
     }
@@ -570,7 +571,7 @@ impl HeapType {
         let _ = engine;
 
         match self {
-            HeapType::Func | HeapType::Concrete(_) | HeapType::NoFunc => HeapType::Func,
+            HeapType::Func | HeapType::ConcreteFunc(_) | HeapType::NoFunc => HeapType::Func,
             HeapType::Extern => HeapType::Extern,
             HeapType::Any | HeapType::I31 | HeapType::None => HeapType::Any,
         }
@@ -589,12 +590,14 @@ impl HeapType {
             (HeapType::Extern, HeapType::Extern) => true,
             (HeapType::Extern, _) => false,
 
-            (HeapType::NoFunc, HeapType::NoFunc | HeapType::Concrete(_) | HeapType::Func) => true,
+            (HeapType::NoFunc, HeapType::NoFunc | HeapType::ConcreteFunc(_) | HeapType::Func) => {
+                true
+            }
             (HeapType::NoFunc, _) => false,
 
-            (HeapType::Concrete(_), HeapType::Func) => true,
-            (HeapType::Concrete(a), HeapType::Concrete(b)) => a.matches(b),
-            (HeapType::Concrete(_), _) => false,
+            (HeapType::ConcreteFunc(_), HeapType::Func) => true,
+            (HeapType::ConcreteFunc(a), HeapType::ConcreteFunc(b)) => a.matches(b),
+            (HeapType::ConcreteFunc(_), _) => false,
 
             (HeapType::Func, HeapType::Func) => true,
             (HeapType::Func, _) => false,
@@ -642,7 +645,7 @@ impl HeapType {
             | HeapType::Any
             | HeapType::I31
             | HeapType::None => true,
-            HeapType::Concrete(ty) => ty.comes_from_same_engine(engine),
+            HeapType::ConcreteFunc(ty) => ty.comes_from_same_engine(engine),
         }
     }
 
@@ -654,8 +657,8 @@ impl HeapType {
             HeapType::Any => WasmHeapType::Any,
             HeapType::I31 => WasmHeapType::I31,
             HeapType::None => WasmHeapType::None,
-            HeapType::Concrete(f) => {
-                WasmHeapType::Concrete(EngineOrModuleTypeIndex::Engine(f.type_index()))
+            HeapType::ConcreteFunc(f) => {
+                WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Engine(f.type_index()))
             }
         }
     }
@@ -668,11 +671,11 @@ impl HeapType {
             WasmHeapType::Any => HeapType::Any,
             WasmHeapType::I31 => HeapType::I31,
             WasmHeapType::None => HeapType::None,
-            WasmHeapType::Concrete(EngineOrModuleTypeIndex::Engine(idx)) => {
-                HeapType::Concrete(FuncType::from_shared_type_index(engine, *idx))
+            WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Engine(idx)) => {
+                HeapType::ConcreteFunc(FuncType::from_shared_type_index(engine, *idx))
             }
-            WasmHeapType::Concrete(EngineOrModuleTypeIndex::Module(_))
-            | WasmHeapType::Concrete(EngineOrModuleTypeIndex::RecGroup(_)) => {
+            WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::Module(_))
+            | WasmHeapType::ConcreteFunc(EngineOrModuleTypeIndex::RecGroup(_)) => {
                 panic!("HeapType::from_wasm_type on non-canonical heap type")
             }
         }
@@ -688,7 +691,7 @@ impl HeapType {
             // TODO: Once we support concrete struct and array types, we will
             // need to inspect the payload to determine whether this is a
             // GC-managed type or not.
-            Self::Concrete(_) => false,
+            Self::ConcreteFunc(_) => false,
 
             // These are compatible with GC references, but don't actually point
             // to GC objecs. It would generally be safe to return `true` here,
@@ -768,7 +771,7 @@ impl ExternType {
                     FuncType::from_shared_type_index(engine, *e).into()
                 }
                 EngineOrModuleTypeIndex::Module(m) => {
-                    FuncType::from_wasm_func_type(engine, types[*m].clone()).into()
+                    FuncType::from_wasm_func_type(engine, types[*m].unwrap_func().clone()).into()
                 }
                 EngineOrModuleTypeIndex::RecGroup(_) => unreachable!(),
             },
@@ -889,6 +892,7 @@ impl FuncType {
     pub fn param(&self, i: usize) -> Option<ValType> {
         let engine = self.engine();
         self.registered_type
+            .unwrap_func()
             .params()
             .get(i)
             .map(|ty| ValType::from_wasm_type(engine, ty))
@@ -899,6 +903,7 @@ impl FuncType {
     pub fn params(&self) -> impl ExactSizeIterator<Item = ValType> + '_ {
         let engine = self.engine();
         self.registered_type
+            .unwrap_func()
             .params()
             .iter()
             .map(|ty| ValType::from_wasm_type(engine, ty))
@@ -910,6 +915,7 @@ impl FuncType {
     pub fn result(&self, i: usize) -> Option<ValType> {
         let engine = self.engine();
         self.registered_type
+            .unwrap_func()
             .returns()
             .get(i)
             .map(|ty| ValType::from_wasm_type(engine, ty))
@@ -920,6 +926,7 @@ impl FuncType {
     pub fn results(&self) -> impl ExactSizeIterator<Item = ValType> + '_ {
         let engine = self.engine();
         self.registered_type
+            .unwrap_func()
             .returns()
             .iter()
             .map(|ty| ValType::from_wasm_type(engine, ty))
@@ -980,7 +987,7 @@ impl FuncType {
     }
 
     pub(crate) fn as_wasm_func_type(&self) -> &WasmFuncType {
-        &self.registered_type
+        self.registered_type.unwrap_func()
     }
 
     pub(crate) fn into_registered_type(self) -> RegisteredType {
@@ -998,7 +1005,16 @@ impl FuncType {
     /// holding a strong reference to all of its types, including any `(ref null
     /// <index>)` types used in the function's parameters and results.
     pub(crate) fn from_wasm_func_type(engine: &Engine, ty: WasmFuncType) -> FuncType {
-        let ty = RegisteredType::new(engine, ty);
+        let ty = RegisteredType::new(
+            engine,
+            WasmSubType {
+                // TODO:
+                //
+                // is_final: true,
+                // supertype: None,
+                composite_type: WasmCompositeType::Func(ty),
+            },
+        );
         Self {
             registered_type: ty,
         }
diff --git a/crates/wasmtime/src/runtime/types/matching.rs b/crates/wasmtime/src/runtime/types/matching.rs
index 45504aceb..9511d9b03 100644
--- a/crates/wasmtime/src/runtime/types/matching.rs
+++ b/crates/wasmtime/src/runtime/types/matching.rs
@@ -1,8 +1,8 @@
 use crate::{linker::DefinitionType, Engine, FuncType};
 use anyhow::{anyhow, bail, Result};
 use wasmtime_environ::{
-    EntityType, Global, Memory, ModuleTypes, Table, TypeTrace, VMSharedTypeIndex, WasmFuncType,
-    WasmHeapType, WasmRefType, WasmValType,
+    EntityType, Global, Memory, ModuleTypes, Table, TypeTrace, VMSharedTypeIndex,
+    WasmCompositeType, WasmHeapType, WasmRefType, WasmSubType, WasmValType,
 };
 
 pub struct MatchCx<'a> {
@@ -36,7 +36,7 @@ impl MatchCx<'_> {
             None => panic!("{actual:?} is not registered"),
         };
 
-        Err(func_ty_mismatch(msg, &expected, &actual))
+        Err(concrete_type_mismatch(msg, &expected, &actual))
     }
 
     /// Validates that the `expected` type matches the type of `actual`
@@ -96,7 +96,7 @@ pub fn entity_ty(
                 if expected == actual {
                     Ok(())
                 } else {
-                    Err(func_ty_mismatch(
+                    Err(concrete_type_mismatch(
                         "function types incompatible",
                         expected,
                         actual,
@@ -109,22 +109,29 @@ pub fn entity_ty(
     }
 }
 
-fn func_ty_mismatch(msg: &str, expected: &WasmFuncType, actual: &WasmFuncType) -> anyhow::Error {
-    let render = |ty: &WasmFuncType| {
-        let params = ty
-            .params()
-            .iter()
-            .map(|s| s.to_string())
-            .collect::<Vec<_>>()
-            .join(", ");
-        let returns = ty
-            .returns()
-            .iter()
-            .map(|s| s.to_string())
-            .collect::<Vec<_>>()
-            .join(", ");
-        format!("`({}) -> ({})`", params, returns)
+fn concrete_type_mismatch(
+    msg: &str,
+    expected: &WasmSubType,
+    actual: &WasmSubType,
+) -> anyhow::Error {
+    let render = |ty: &WasmSubType| match &ty.composite_type {
+        WasmCompositeType::Func(ty) => {
+            let params = ty
+                .params()
+                .iter()
+                .map(|s| s.to_string())
+                .collect::<Vec<_>>()
+                .join(", ");
+            let returns = ty
+                .returns()
+                .iter()
+                .map(|s| s.to_string())
+                .collect::<Vec<_>>()
+                .join(", ");
+            format!("`({}) -> ({})`", params, returns)
+        }
     };
+
     anyhow!(
         "{msg}: expected func of type {}, found func of type {}",
         render(expected),
@@ -197,15 +204,15 @@ fn match_heap(expected: WasmHeapType, actual: WasmHeapType, desc: &str) -> Resul
     let result = match (actual, expected) {
         // TODO: Wasm GC introduces subtyping between function types, so it will
         // no longer suffice to check whether canonicalized type IDs are equal.
-        (H::Concrete(actual), H::Concrete(expected)) => actual == expected,
+        (H::ConcreteFunc(actual), H::ConcreteFunc(expected)) => actual == expected,
 
         (H::NoFunc, H::NoFunc) => true,
         (_, H::NoFunc) => false,
 
-        (H::NoFunc, H::Concrete(_)) => true,
-        (_, H::Concrete(_)) => false,
+        (H::NoFunc, H::ConcreteFunc(_)) => true,
+        (_, H::ConcreteFunc(_)) => false,
 
-        (H::NoFunc | H::Concrete(_) | H::Func, H::Func) => true,
+        (H::NoFunc | H::ConcreteFunc(_) | H::Func, H::Func) => true,
         (_, H::Func) => false,
 
         (H::Extern, H::Extern) => true,
diff --git a/crates/wasmtime/src/runtime/values.rs b/crates/wasmtime/src/runtime/values.rs
index 96f21a581..0b03ade19 100644
--- a/crates/wasmtime/src/runtime/values.rs
+++ b/crates/wasmtime/src/runtime/values.rs
@@ -80,7 +80,7 @@ impl Val {
     /// Returns the null reference for the given heap type.
     pub fn null_ref(heap_type: HeapType) -> Val {
         match heap_type {
-            HeapType::Func | HeapType::NoFunc | HeapType::Concrete(_) => Val::FuncRef(None),
+            HeapType::Func | HeapType::NoFunc | HeapType::ConcreteFunc(_) => Val::FuncRef(None),
             HeapType::Extern => Val::ExternRef(None),
             HeapType::Any | HeapType::I31 | HeapType::None => Val::AnyRef(None),
         }
@@ -129,9 +129,10 @@ impl Val {
             Val::V128(_) => ValType::V128,
             Val::ExternRef(_) => ValType::EXTERNREF,
             Val::FuncRef(None) => ValType::NULLFUNCREF,
-            Val::FuncRef(Some(f)) => {
-                ValType::Ref(RefType::new(false, HeapType::Concrete(f.load_ty(store))))
-            }
+            Val::FuncRef(Some(f)) => ValType::Ref(RefType::new(
+                false,
+                HeapType::ConcreteFunc(f.load_ty(store)),
+            )),
             Val::AnyRef(None) => ValType::NULLREF,
             Val::AnyRef(Some(_)) => {
                 assert!(VMGcRef::ONLY_EXTERN_REF_AND_I31);
@@ -242,7 +243,7 @@ impl Val {
             ValType::V128 => Val::V128(raw.get_v128().into()),
             ValType::Ref(ref_ty) => {
                 let ref_ = match ref_ty.heap_type() {
-                    HeapType::Func | HeapType::Concrete(_) => {
+                    HeapType::Func | HeapType::ConcreteFunc(_) => {
                         Func::from_raw(store, raw.get_funcref()).into()
                     }
                     HeapType::NoFunc => Ref::Func(None),
@@ -757,7 +758,7 @@ impl Ref {
                 // NB: We choose the most-specific heap type we can here and let
                 // subtyping do its thing if callers are matching against a
                 // `HeapType::Func`.
-                Ref::Func(Some(f)) => HeapType::Concrete(f.load_ty(store)),
+                Ref::Func(Some(f)) => HeapType::ConcreteFunc(f.load_ty(store)),
                 Ref::Func(None) => HeapType::NoFunc,
 
                 Ref::Any(Some(_)) => {
@@ -791,8 +792,8 @@ impl Ref {
             (Ref::Extern(_), _) => false,
 
             (Ref::Func(_), HeapType::Func) => true,
-            (Ref::Func(None), HeapType::NoFunc | HeapType::Concrete(_)) => true,
-            (Ref::Func(Some(f)), HeapType::Concrete(func_ty)) => f._matches_ty(store, func_ty),
+            (Ref::Func(None), HeapType::NoFunc | HeapType::ConcreteFunc(_)) => true,
+            (Ref::Func(Some(f)), HeapType::ConcreteFunc(func_ty)) => f._matches_ty(store, func_ty),
             (Ref::Func(_), _) => false,
 
             (Ref::Any(_), HeapType::Any) => true,
@@ -923,7 +924,7 @@ mod tests {
         // Should panic.
         let _ = Val::FuncRef(Some(f)).matches_ty(
             &s1,
-            &ValType::Ref(RefType::new(true, HeapType::Concrete(t2))),
+            &ValType::Ref(RefType::new(true, HeapType::ConcreteFunc(t2))),
         );
     }
 
@@ -940,6 +941,6 @@ mod tests {
         let f = Func::new(&mut s1, t1.clone(), |_caller, _args, _results| Ok(()));
 
         // Should panic.
-        let _ = Ref::Func(Some(f)).matches_ty(&s1, &RefType::new(true, HeapType::Concrete(t2)));
+        let _ = Ref::Func(Some(f)).matches_ty(&s1, &RefType::new(true, HeapType::ConcreteFunc(t2)));
     }
 }
diff --git a/crates/winch/src/compiler.rs b/crates/winch/src/compiler.rs
index ecb93dacd..217f3daff 100644
--- a/crates/winch/src/compiler.rs
+++ b/crates/winch/src/compiler.rs
@@ -94,7 +94,7 @@ impl wasmtime_environ::Compiler for Compiler {
     ) -> Result<(WasmFunctionInfo, Box<dyn Any + Send>), CompileError> {
         let index = translation.module.func_index(index);
         let sig = translation.module.functions[index].signature;
-        let ty = &types[sig];
+        let ty = types[sig].unwrap_func();
         let FunctionBodyData { body, validator } = data;
         let mut context = self.get_context(translation);
         let mut validator = validator.into_validator(mem::take(&mut context.allocations));
diff --git a/winch/codegen/src/codegen/env.rs b/winch/codegen/src/codegen/env.rs
index 6818c30f8..a571f2b38 100644
--- a/winch/codegen/src/codegen/env.rs
+++ b/winch/codegen/src/codegen/env.rs
@@ -198,7 +198,7 @@ impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
             }
             FuncType(idx) => {
                 let sig_index = self.translation.module.types[TypeIndex::from_u32(idx)];
-                let sig = &self.types[sig_index];
+                let sig = self.types[sig_index].unwrap_func();
                 BlockSig::new(control::BlockType::func(sig.clone()))
             }
         }
@@ -340,7 +340,7 @@ impl<'a, 'translation, 'data, P: PtrSize> FuncEnv<'a, 'translation, 'data, P> {
             Callee::FuncRef(idx) => {
                 let val = || {
                     let sig_index = self.translation.module.types[*idx];
-                    let ty = &self.types[sig_index];
+                    let ty = self.types[sig_index].unwrap_func();
                     let sig = wasm_sig::<A>(ty);
                     sig
                 };


Test patch:
diff --git a/tests/all/externals.rs b/tests/all/externals.rs
index be89ef934..d6e5878a8 100644
--- a/tests/all/externals.rs
+++ b/tests/all/externals.rs
@@ -433,7 +433,7 @@ fn dummy_funcs_and_subtypes(
         None,
         Some(ValType::Ref(RefType::new(
             true,
-            HeapType::Concrete(FuncType::new(&engine, None, None)),
+            HeapType::ConcreteFunc(FuncType::new(&engine, None, None)),
         ))),
     );
     let b = Func::new(
diff --git a/tests/all/func.rs b/tests/all/func.rs
index 30cebd6d4..ff69e172a 100644
--- a/tests/all/func.rs
+++ b/tests/all/func.rs
@@ -1773,7 +1773,10 @@ fn call_wasm_passing_subtype_func_param() -> anyhow::Result<()> {
     let h_ty = FuncType::new(
         &engine,
         None,
-        Some(ValType::Ref(RefType::new(true, HeapType::Concrete(g_ty)))),
+        Some(ValType::Ref(RefType::new(
+            true,
+            HeapType::ConcreteFunc(g_ty),
+        ))),
     );
     let h = Func::new(&mut store, h_ty, move |_caller, _params, results| {
         results[0] = Val::FuncRef(Some(g.clone()));
diff --git a/tests/all/linker.rs b/tests/all/linker.rs
index 16722af2b..222f7f38a 100644
--- a/tests/all/linker.rs
+++ b/tests/all/linker.rs
@@ -461,13 +461,13 @@ fn linker_instantiate_with_concrete_func_refs() -> Result<()> {
     )?;
 
     let a = FuncType::new(&engine, None, Some(ValType::I32));
-    let ref_null_a = ValType::from(RefType::new(true, HeapType::Concrete(a.clone())));
+    let ref_null_a = ValType::from(RefType::new(true, HeapType::ConcreteFunc(a.clone())));
 
     let b = FuncType::new(&engine, None, Some(ref_null_a));
-    let ref_null_b = ValType::from(RefType::new(true, HeapType::Concrete(b.clone())));
+    let ref_null_b = ValType::from(RefType::new(true, HeapType::ConcreteFunc(b.clone())));
 
     let c = FuncType::new(&engine, None, Some(ref_null_b));
-    let ref_null_c = ValType::from(RefType::new(true, HeapType::Concrete(c.clone())));
+    let ref_null_c = ValType::from(RefType::new(true, HeapType::ConcreteFunc(c.clone())));
 
     let mut store = Store::new(&engine, ());
     let a_func = Func::new(&mut store, a, |_caller, _args, results| {
@@ -540,7 +540,7 @@ fn linker_defines_func_subtype() -> Result<()> {
         |_caller, _args, _results| Ok(()),
     )?;
     let nop_ty = FuncType::new(&engine, None, None);
-    let ref_null_nop = ValType::from(RefType::new(true, HeapType::Concrete(nop_ty)));
+    let ref_null_nop = ValType::from(RefType::new(true, HeapType::ConcreteFunc(nop_ty)));
     linker.func_new(
         "env",
         "h",
@@ -676,7 +676,7 @@ fn linker_defines_global_subtype_mut_err() -> Result<()> {
     // Not mutable.
     let mut linker = Linker::new(&engine);
     let nop = FuncType::new(&engine, None, None);
-    let ref_null_nop = ValType::from(RefType::new(true, HeapType::Concrete(nop)));
+    let ref_null_nop = ValType::from(RefType::new(true, HeapType::ConcreteFunc(nop)));
     let g = Global::new(
         &mut store,
         GlobalType::new(ref_null_nop, Mutability::Const),