Repository: bytecodealliance/wasm-tools
Pull request #1613 (2024-06-12 14:48:22)

 Improve filtering for return_call_indirect with wasm-smith 
Add to the `call_indirect` filter that the selected function type must
have the exact same return signature as the function doing the call.

Closes https://github.com/bytecodealliance/wasm-tools/issues/1612


Patch:
diff --git a/crates/wasm-encoder/src/core/types.rs b/crates/wasm-encoder/src/core/types.rs
index 8b5f340b..0fa12f68 100644
--- a/crates/wasm-encoder/src/core/types.rs
+++ b/crates/wasm-encoder/src/core/types.rs
@@ -344,33 +344,91 @@ pub struct RefType {
 }
 
 impl RefType {
-    /// Alias for the `funcref` type in WebAssembly
+    /// Alias for the `anyref` type in WebAssembly.
+    pub const ANYREF: RefType = RefType {
+        nullable: true,
+        heap_type: HeapType::Abstract {
+            shared: false,
+            ty: AbstractHeapType::Any,
+        },
+    };
+
+    /// Alias for the `anyref` type in WebAssembly.
+    pub const EQREF: RefType = RefType {
+        nullable: true,
+        heap_type: HeapType::Abstract {
+            shared: false,
+            ty: AbstractHeapType::Eq,
+        },
+    };
+
+    /// Alias for the `funcref` type in WebAssembly.
     pub const FUNCREF: RefType = RefType {
         nullable: true,
-        heap_type: HeapType::Func,
+        heap_type: HeapType::Abstract {
+            shared: false,
+            ty: AbstractHeapType::Func,
+        },
     };
 
-    /// Alias for the `externref` type in WebAssembly
+    /// Alias for the `externref` type in WebAssembly.
     pub const EXTERNREF: RefType = RefType {
         nullable: true,
-        heap_type: HeapType::Extern,
+        heap_type: HeapType::Abstract {
+            shared: false,
+            ty: AbstractHeapType::Extern,
+        },
     };
 
-    /// Alias for the `exnref` type in WebAssembly
+    /// Alias for the `i31ref` type in WebAssembly.
+    pub const I31REF: RefType = RefType {
+        nullable: true,
+        heap_type: HeapType::Abstract {
+            shared: false,
+            ty: AbstractHeapType::I31,
+        },
+    };
+
+    /// Alias for the `arrayref` type in WebAssembly.
+    pub const ARRAYREF: RefType = RefType {
+        nullable: true,
+        heap_type: HeapType::Abstract {
+            shared: false,
+            ty: AbstractHeapType::Array,
+        },
+    };
+
+    /// Alias for the `exnref` type in WebAssembly.
     pub const EXNREF: RefType = RefType {
         nullable: true,
-        heap_type: HeapType::Exn,
+        heap_type: HeapType::Abstract {
+            shared: false,
+            ty: AbstractHeapType::Exn,
+        },
     };
+
+    /// Set the nullability of this reference type.
+    pub fn nullable(mut self, nullable: bool) -> Self {
+        self.nullable = nullable;
+        self
+    }
 }
 
 impl Encode for RefType {
     fn encode(&self, sink: &mut Vec<u8>) {
         if self.nullable {
             // Favor the original encodings of `funcref` and `externref` where
-            // possible
+            // possible.
+            use AbstractHeapType::*;
             match self.heap_type {
-                HeapType::Func => return sink.push(0x70),
-                HeapType::Extern => return sink.push(0x6f),
+                HeapType::Abstract {
+                    shared: false,
+                    ty: Func,
+                } => return sink.push(0x70),
+                HeapType::Abstract {
+                    shared: false,
+                    ty: Extern,
+                } => return sink.push(0x6f),
                 _ => {}
             }
         }
@@ -405,6 +463,78 @@ impl From<RefType> for ValType {
 /// Part of the function references proposal.
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
 pub enum HeapType {
+    /// An abstract heap type; e.g., `anyref`.
+    Abstract {
+        /// Whether the type is shared.
+        shared: bool,
+        /// The actual heap type.
+        ty: AbstractHeapType,
+    },
+
+    /// A concrete Wasm-defined type at the given index.
+    Concrete(u32),
+}
+
+impl HeapType {
+    /// Alias for the unshared `any` heap type.
+    pub const ANY: Self = Self::Abstract {
+        shared: false,
+        ty: AbstractHeapType::Any,
+    };
+
+    /// Alias for the unshared `func` heap type.
+    pub const FUNC: Self = Self::Abstract {
+        shared: false,
+        ty: AbstractHeapType::Func,
+    };
+
+    /// Alias for the unshared `extern` heap type.
+    pub const EXTERN: Self = Self::Abstract {
+        shared: false,
+        ty: AbstractHeapType::Extern,
+    };
+
+    /// Alias for the unshared `i31` heap type.
+    pub const I31: Self = Self::Abstract {
+        shared: false,
+        ty: AbstractHeapType::I31,
+    };
+}
+
+impl Encode for HeapType {
+    fn encode(&self, sink: &mut Vec<u8>) {
+        match self {
+            HeapType::Abstract { shared, ty } => {
+                if *shared {
+                    sink.push(0x65);
+                }
+                ty.encode(sink);
+            }
+            // Note that this is encoded as a signed type rather than unsigned
+            // as it's decoded as an s33
+            HeapType::Concrete(i) => i64::from(*i).encode(sink),
+        }
+    }
+}
+
+#[cfg(feature = "wasmparser")]
+impl TryFrom<wasmparser::HeapType> for HeapType {
+    type Error = ();
+
+    fn try_from(heap_type: wasmparser::HeapType) -> Result<Self, Self::Error> {
+        Ok(match heap_type {
+            wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.as_module_index().ok_or(())?),
+            wasmparser::HeapType::Abstract { shared, ty } => HeapType::Abstract {
+                shared,
+                ty: ty.into(),
+            },
+        })
+    }
+}
+
+/// An abstract heap type.
+#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Ord, PartialOrd)]
+pub enum AbstractHeapType {
     /// Untyped (any) function.
     Func,
 
@@ -455,53 +585,46 @@ pub enum HeapType {
 
     /// The abstract `noexn` heap type.
     NoExn,
-
-    /// A concrete Wasm-defined type at the given index.
-    Concrete(u32),
 }
 
-impl Encode for HeapType {
+impl Encode for AbstractHeapType {
     fn encode(&self, sink: &mut Vec<u8>) {
+        use AbstractHeapType::*;
         match self {
-            HeapType::Func => sink.push(0x70),
-            HeapType::Extern => sink.push(0x6F),
-            HeapType::Any => sink.push(0x6E),
-            HeapType::None => sink.push(0x71),
-            HeapType::NoExtern => sink.push(0x72),
-            HeapType::NoFunc => sink.push(0x73),
-            HeapType::Eq => sink.push(0x6D),
-            HeapType::Struct => sink.push(0x6B),
-            HeapType::Array => sink.push(0x6A),
-            HeapType::I31 => sink.push(0x6C),
-            HeapType::Exn => sink.push(0x69),
-            HeapType::NoExn => sink.push(0x74),
-            // Note that this is encoded as a signed type rather than unsigned
-            // as it's decoded as an s33
-            HeapType::Concrete(i) => i64::from(*i).encode(sink),
+            Func => sink.push(0x70),
+            Extern => sink.push(0x6F),
+            Any => sink.push(0x6E),
+            None => sink.push(0x71),
+            NoExtern => sink.push(0x72),
+            NoFunc => sink.push(0x73),
+            Eq => sink.push(0x6D),
+            Struct => sink.push(0x6B),
+            Array => sink.push(0x6A),
+            I31 => sink.push(0x6C),
+            Exn => sink.push(0x69),
+            NoExn => sink.push(0x74),
         }
     }
 }
 
 #[cfg(feature = "wasmparser")]
-impl TryFrom<wasmparser::HeapType> for HeapType {
-    type Error = ();
-
-    fn try_from(heap_type: wasmparser::HeapType) -> Result<Self, Self::Error> {
-        Ok(match heap_type {
-            wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.as_module_index().ok_or(())?),
-            wasmparser::HeapType::Func => HeapType::Func,
-            wasmparser::HeapType::Extern => HeapType::Extern,
-            wasmparser::HeapType::Any => HeapType::Any,
-            wasmparser::HeapType::None => HeapType::None,
-            wasmparser::HeapType::NoExtern => HeapType::NoExtern,
-            wasmparser::HeapType::NoFunc => HeapType::NoFunc,
-            wasmparser::HeapType::Eq => HeapType::Eq,
-            wasmparser::HeapType::Struct => HeapType::Struct,
-            wasmparser::HeapType::Array => HeapType::Array,
-            wasmparser::HeapType::I31 => HeapType::I31,
-            wasmparser::HeapType::Exn => HeapType::Exn,
-            wasmparser::HeapType::NoExn => HeapType::NoExn,
-        })
+impl From<wasmparser::AbstractHeapType> for AbstractHeapType {
+    fn from(value: wasmparser::AbstractHeapType) -> Self {
+        use wasmparser::AbstractHeapType::*;
+        match value {
+            Func => AbstractHeapType::Func,
+            Extern => AbstractHeapType::Extern,
+            Any => AbstractHeapType::Any,
+            None => AbstractHeapType::None,
+            NoExtern => AbstractHeapType::NoExtern,
+            NoFunc => AbstractHeapType::NoFunc,
+            Eq => AbstractHeapType::Eq,
+            Struct => AbstractHeapType::Struct,
+            Array => AbstractHeapType::Array,
+            I31 => AbstractHeapType::I31,
+            Exn => AbstractHeapType::Exn,
+            NoExn => AbstractHeapType::NoExn,
+        }
     }
 }
 
diff --git a/crates/wasm-mutate/Cargo.toml b/crates/wasm-mutate/Cargo.toml
index 6a96c9ed..3a9385cd 100644
--- a/crates/wasm-mutate/Cargo.toml
+++ b/crates/wasm-mutate/Cargo.toml
@@ -14,7 +14,7 @@ workspace = true
 clap = { workspace = true, optional = true }
 thiserror = "1.0.28"
 wasmparser = { workspace = true }
-wasm-encoder = { workspace = true }
+wasm-encoder = { workspace = true, features = ["wasmparser"] }
 rand = { workspace = true }
 log = { workspace = true }
 egg = "0.6.0"
diff --git a/crates/wasm-mutate/src/module.rs b/crates/wasm-mutate/src/module.rs
index 87653dd7..d6ef3846 100644
--- a/crates/wasm-mutate/src/module.rs
+++ b/crates/wasm-mutate/src/module.rs
@@ -80,19 +80,11 @@ pub fn map_ref_type(ref_ty: wasmparser::RefType) -> Result<RefType> {
     Ok(RefType {
         nullable: ref_ty.is_nullable(),
         heap_type: match ref_ty.heap_type() {
-            wasmparser::HeapType::Func => HeapType::Func,
-            wasmparser::HeapType::Extern => HeapType::Extern,
-            wasmparser::HeapType::Any => HeapType::Any,
-            wasmparser::HeapType::None => HeapType::None,
-            wasmparser::HeapType::NoExtern => HeapType::NoExtern,
-            wasmparser::HeapType::NoFunc => HeapType::NoFunc,
-            wasmparser::HeapType::Eq => HeapType::Eq,
-            wasmparser::HeapType::Struct => HeapType::Struct,
-            wasmparser::HeapType::Array => HeapType::Array,
-            wasmparser::HeapType::I31 => HeapType::I31,
-            wasmparser::HeapType::Exn => HeapType::Exn,
-            wasmparser::HeapType::NoExn => HeapType::NoExn,
             wasmparser::HeapType::Concrete(i) => HeapType::Concrete(i.as_module_index().unwrap()),
+            wasmparser::HeapType::Abstract { shared, ty } => {
+                let ty = ty.into();
+                HeapType::Abstract { shared, ty }
+            }
         },
     })
 }
diff --git a/crates/wasm-mutate/src/mutators/add_function.rs b/crates/wasm-mutate/src/mutators/add_function.rs
index 2de031fd..7165f367 100644
--- a/crates/wasm-mutate/src/mutators/add_function.rs
+++ b/crates/wasm-mutate/src/mutators/add_function.rs
@@ -4,7 +4,7 @@ use super::Mutator;
 use crate::module::{PrimitiveTypeInfo, TypeInfo};
 use crate::{Result, WasmMutate};
 use rand::Rng;
-use wasm_encoder::{HeapType, Instruction, Module};
+use wasm_encoder::{AbstractHeapType, HeapType, Instruction, Module};
 
 /// Mutator that adds new, empty functions to a Wasm module.
 #[derive(Clone, Copy)]
@@ -62,10 +62,16 @@ impl Mutator for AddFunctionMutator {
                     func.instruction(&Instruction::V128Const(0));
                 }
                 PrimitiveTypeInfo::FuncRef => {
-                    func.instruction(&Instruction::RefNull(HeapType::Func));
+                    func.instruction(&Instruction::RefNull(HeapType::Abstract {
+                        shared: false,
+                        ty: AbstractHeapType::Func,
+                    }));
                 }
                 PrimitiveTypeInfo::ExternRef => {
-                    func.instruction(&Instruction::RefNull(HeapType::Extern));
+                    func.instruction(&Instruction::RefNull(HeapType::Abstract {
+                        shared: false,
+                        ty: AbstractHeapType::Extern,
+                    }));
                 }
                 PrimitiveTypeInfo::Empty => unreachable!(),
             }
diff --git a/crates/wasm-mutate/src/mutators/modify_const_exprs.rs b/crates/wasm-mutate/src/mutators/modify_const_exprs.rs
index 7b7fb905..173f53cf 100644
--- a/crates/wasm-mutate/src/mutators/modify_const_exprs.rs
+++ b/crates/wasm-mutate/src/mutators/modify_const_exprs.rs
@@ -124,8 +124,14 @@ impl<'cfg, 'wasm> Translator for InitTranslator<'cfg, 'wasm> {
                 } else {
                     f64::from_bits(self.config.rng().gen())
                 }),
-                T::FUNCREF => CE::ref_null(wasm_encoder::HeapType::Func),
-                T::EXTERNREF => CE::ref_null(wasm_encoder::HeapType::Extern),
+                T::FUNCREF => CE::ref_null(wasm_encoder::HeapType::Abstract {
+                    shared: false,
+                    ty: wasm_encoder::AbstractHeapType::Func,
+                }),
+                T::EXTERNREF => CE::ref_null(wasm_encoder::HeapType::Abstract {
+                    shared: false,
+                    ty: wasm_encoder::AbstractHeapType::Func,
+                }),
                 T::Ref(_) => unimplemented!(),
             }
         } else {
diff --git a/crates/wasm-mutate/src/mutators/peephole/dfg.rs b/crates/wasm-mutate/src/mutators/peephole/dfg.rs
index c69391b3..a5e1ca2a 100644
--- a/crates/wasm-mutate/src/mutators/peephole/dfg.rs
+++ b/crates/wasm-mutate/src/mutators/peephole/dfg.rs
@@ -758,12 +758,12 @@ impl<'a> DFGBuilder {
                 }
 
                 Operator::RefNull {
-                    hty: wasmparser::HeapType::Extern,
+                    hty: wasmparser::HeapType::EXTERN,
                 } => {
                     self.push_node(Lang::RefNull(RefType::Extern), idx);
                 }
                 Operator::RefNull {
-                    hty: wasmparser::HeapType::Func,
+                    hty: wasmparser::HeapType::FUNC,
                 } => {
                     self.push_node(Lang::RefNull(RefType::Func), idx);
                 }
diff --git a/crates/wasm-mutate/src/mutators/peephole/eggsy/lang.rs b/crates/wasm-mutate/src/mutators/peephole/eggsy/lang.rs
index e5bb831b..db4d9210 100644
--- a/crates/wasm-mutate/src/mutators/peephole/eggsy/lang.rs
+++ b/crates/wasm-mutate/src/mutators/peephole/eggsy/lang.rs
@@ -3,7 +3,7 @@
 use egg::Id;
 use std::fmt::{self, Display};
 use std::str::FromStr;
-use wasm_encoder::HeapType;
+use wasm_encoder::{AbstractHeapType, HeapType};
 
 /// This is a macro used to define the `Lang` enum.
 ///
@@ -1072,8 +1072,14 @@ pub enum RefType {
 impl From<RefType> for HeapType {
     fn from(rt: RefType) -> Self {
         match rt {
-            RefType::Func => HeapType::Func,
-            RefType::Extern => HeapType::Extern,
+            RefType::Func => HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::Func,
+            },
+            RefType::Extern => HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::Extern,
+            },
         }
     }
 }
diff --git a/crates/wasm-mutate/src/mutators/snip_function.rs b/crates/wasm-mutate/src/mutators/snip_function.rs
index 1ac67890..00f86e92 100644
--- a/crates/wasm-mutate/src/mutators/snip_function.rs
+++ b/crates/wasm-mutate/src/mutators/snip_function.rs
@@ -61,10 +61,10 @@ impl Mutator for SnipMutator {
                                 f.instruction(&Instruction::V128Const(0));
                             }
                             PrimitiveTypeInfo::FuncRef => {
-                                f.instruction(&Instruction::RefNull(HeapType::Func));
+                                f.instruction(&Instruction::RefNull(HeapType::FUNC));
                             }
                             PrimitiveTypeInfo::ExternRef => {
-                                f.instruction(&Instruction::RefNull(HeapType::Extern));
+                                f.instruction(&Instruction::RefNull(HeapType::EXTERN));
                             }
                             PrimitiveTypeInfo::Empty => {
                                 unreachable!()
diff --git a/crates/wasm-mutate/src/mutators/translate.rs b/crates/wasm-mutate/src/mutators/translate.rs
index 5c5fbbb2..839879bc 100644
--- a/crates/wasm-mutate/src/mutators/translate.rs
+++ b/crates/wasm-mutate/src/mutators/translate.rs
@@ -210,21 +210,16 @@ pub fn refty(t: &mut dyn Translator, ty: &wasmparser::RefType) -> Result<RefType
 
 pub fn heapty(t: &mut dyn Translator, ty: &wasmparser::HeapType) -> Result<HeapType> {
     match ty {
-        wasmparser::HeapType::Func => Ok(HeapType::Func),
-        wasmparser::HeapType::Extern => Ok(HeapType::Extern),
-        wasmparser::HeapType::Any => Ok(HeapType::Any),
-        wasmparser::HeapType::None => Ok(HeapType::None),
-        wasmparser::HeapType::NoExtern => Ok(HeapType::NoExtern),
-        wasmparser::HeapType::NoFunc => Ok(HeapType::NoFunc),
-        wasmparser::HeapType::Eq => Ok(HeapType::Eq),
-        wasmparser::HeapType::Struct => Ok(HeapType::Struct),
-        wasmparser::HeapType::Array => Ok(HeapType::Array),
-        wasmparser::HeapType::I31 => Ok(HeapType::I31),
-        wasmparser::HeapType::Exn => Ok(HeapType::Exn),
-        wasmparser::HeapType::NoExn => Ok(HeapType::NoExn),
         wasmparser::HeapType::Concrete(i) => Ok(HeapType::Concrete(
             t.remap(Item::Type, i.as_module_index().unwrap())?,
         )),
+        wasmparser::HeapType::Abstract { shared, ty } => {
+            let ty = (*ty).into();
+            Ok(HeapType::Abstract {
+                shared: *shared,
+                ty,
+            })
+        }
     }
 }
 
@@ -251,7 +246,11 @@ pub fn const_expr(
         match op {
             Operator::RefFunc { .. }
             | Operator::RefNull {
-                hty: wasmparser::HeapType::Func,
+                hty:
+                    wasmparser::HeapType::Abstract {
+                        ty: wasmparser::AbstractHeapType::Func,
+                        ..
+                    },
                 ..
             }
             | Operator::GlobalGet { .. } => {}
diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs
index 263c294e..ede06314 100644
--- a/crates/wasm-smith/src/core.rs
+++ b/crates/wasm-smith/src/core.rs
@@ -15,8 +15,8 @@ use std::ops::Range;
 use std::rc::Rc;
 use std::str::{self, FromStr};
 use wasm_encoder::{
-    ArrayType, BlockType, ConstExpr, ExportKind, FieldType, HeapType, RefType, StorageType,
-    StructType, ValType,
+    AbstractHeapType, ArrayType, BlockType, ConstExpr, ExportKind, FieldType, HeapType, RefType,
+    StorageType, StructType, ValType,
 };
 pub(crate) use wasm_encoder::{GlobalType, MemoryType, TableType};
 
@@ -449,31 +449,69 @@ impl Module {
     }
 
     fn heap_type_is_sub_type(&self, a: HeapType, b: HeapType) -> bool {
+        use AbstractHeapType::*;
         use HeapType as HT;
         match (a, b) {
             (a, b) if a == b => true,
 
-            (HT::Eq | HT::I31 | HT::Struct | HT::Array | HT::None, HT::Any) => true,
-            (HT::I31 | HT::Struct | HT::Array | HT::None, HT::Eq) => true,
-            (HT::NoExtern, HT::Extern) => true,
-            (HT::NoFunc, HT::Func) => true,
-            (HT::None, HT::I31 | HT::Array | HT::Struct) => true,
-
-            (HT::Concrete(a), HT::Eq | HT::Any) => matches!(
-                self.ty(a).composite_type,
-                CompositeType::Array(_) | CompositeType::Struct(_)
-            ),
-
-            (HT::Concrete(a), HT::Struct) => {
-                matches!(self.ty(a).composite_type, CompositeType::Struct(_))
+            (
+                HT::Abstract {
+                    shared: a_shared,
+                    ty: a_ty,
+                },
+                HT::Abstract {
+                    shared: b_shared,
+                    ty: b_ty,
+                },
+            ) => {
+                a_shared == b_shared
+                    && match (a_ty, b_ty) {
+                        (Eq | I31 | Struct | Array | None, Any) => true,
+                        (I31 | Struct | Array | None, Eq) => true,
+                        (NoExtern, Extern) => true,
+                        (NoFunc, Func) => true,
+                        (None, I31 | Array | Struct) => true,
+                        (NoExn, Exn) => true,
+                        _ => false,
+                    }
             }
 
-            (HT::Concrete(a), HT::Array) => {
-                matches!(self.ty(a).composite_type, CompositeType::Array(_))
+            (HT::Concrete(a), HT::Abstract { shared, ty }) => {
+                if shared {
+                    // TODO: handle shared
+                    todo!("check shared-ness of concrete type");
+                }
+                match ty {
+                    Eq | Any => matches!(
+                        self.ty(a).composite_type,
+                        CompositeType::Array(_) | CompositeType::Struct(_)
+                    ),
+                    Struct => {
+                        matches!(self.ty(a).composite_type, CompositeType::Struct(_))
+                    }
+                    Array => {
+                        matches!(self.ty(a).composite_type, CompositeType::Array(_))
+                    }
+                    Func => {
+                        matches!(self.ty(a).composite_type, CompositeType::Func(_))
+                    }
+                    _ => false,
+                }
             }
 
-            (HT::Concrete(a), HT::Func) => {
-                matches!(self.ty(a).composite_type, CompositeType::Func(_))
+            (HT::Abstract { shared, ty }, HT::Concrete(b)) => {
+                if shared {
+                    // TODO: handle shared
+                    todo!("check shared-ness of concrete type");
+                }
+                match ty {
+                    None => matches!(
+                        self.ty(b).composite_type,
+                        CompositeType::Array(_) | CompositeType::Struct(_)
+                    ),
+                    NoFunc => matches!(self.ty(b).composite_type, CompositeType::Func(_)),
+                    _ => false,
+                }
             }
 
             (HT::Concrete(mut a), HT::Concrete(b)) => loop {
@@ -486,33 +524,6 @@ impl Module {
                     return false;
                 }
             },
-
-            (HT::None, HT::Concrete(b)) => matches!(
-                self.ty(b).composite_type,
-                CompositeType::Array(_) | CompositeType::Struct(_)
-            ),
-
-            (HT::NoFunc, HT::Concrete(b)) => {
-                matches!(self.ty(b).composite_type, CompositeType::Func(_))
-            }
-
-            (HT::NoExn, HT::Exn) => true,
-
-            // Nothing else matches. (Avoid full wildcard matches so that
-            // adding/modifying variants is easier in the future.)
-            (HT::Concrete(_), _)
-            | (HT::Func, _)
-            | (HT::Extern, _)
-            | (HT::Any, _)
-            | (HT::None, _)
-            | (HT::NoExtern, _)
-            | (HT::NoFunc, _)
-            | (HT::Eq, _)
-            | (HT::Struct, _)
-            | (HT::Array, _)
-            | (HT::I31, _)
-            | (HT::Exn, _)
-            | (HT::NoExn, _) => false,
         }
     }
 
@@ -731,26 +742,40 @@ impl Module {
         use HeapType as HT;
         let mut choices = vec![ty];
         match ty {
-            HT::Any => {
-                choices.extend([HT::Eq, HT::Struct, HT::Array, HT::I31, HT::None]);
-                choices.extend(self.array_types.iter().copied().map(HT::Concrete));
-                choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
-            }
-            HT::Eq => {
-                choices.extend([HT::Struct, HT::Array, HT::I31, HT::None]);
-                choices.extend(self.array_types.iter().copied().map(HT::Concrete));
-                choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
-            }
-            HT::Struct => {
-                choices.extend([HT::Struct, HT::None]);
-                choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
-            }
-            HT::Array => {
-                choices.extend([HT::Array, HT::None]);
-                choices.extend(self.array_types.iter().copied().map(HT::Concrete));
-            }
-            HT::I31 => {
-                choices.push(HT::None);
+            HT::Abstract { shared, ty } => {
+                use AbstractHeapType::*;
+                let ht = |ty| HT::Abstract { shared, ty };
+                match ty {
+                    Any => {
+                        choices.extend([ht(Eq), ht(Struct), ht(Array), ht(I31), ht(None)]);
+                        choices.extend(self.array_types.iter().copied().map(HT::Concrete));
+                        choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
+                    }
+                    Eq => {
+                        choices.extend([ht(Struct), ht(Array), ht(I31), ht(None)]);
+                        choices.extend(self.array_types.iter().copied().map(HT::Concrete));
+                        choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
+                    }
+                    Struct => {
+                        choices.extend([ht(Struct), ht(None)]);
+                        choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
+                    }
+                    Array => {
+                        choices.extend([ht(Array), ht(None)]);
+                        choices.extend(self.array_types.iter().copied().map(HT::Concrete));
+                    }
+                    I31 => {
+                        choices.push(ht(None));
+                    }
+                    Func => {
+                        choices.extend(self.func_types.iter().copied().map(HT::Concrete));
+                        choices.push(ht(NoFunc));
+                    }
+                    Extern => {
+                        choices.push(ht(NoExtern));
+                    }
+                    Exn | NoExn | None | NoExtern | NoFunc => {}
+                }
             }
             HT::Concrete(idx) => {
                 if let Some(subs) = self.super_to_sub_types.get(&idx) {
@@ -762,9 +787,15 @@ impl Module {
                     .map(|ty| &ty.composite_type)
                 {
                     Some(CompositeType::Array(_)) | Some(CompositeType::Struct(_)) => {
-                        choices.push(HT::None)
+                        choices.push(HT::Abstract {
+                            shared: false, // TODO: handle shared
+                            ty: AbstractHeapType::None,
+                        })
                     }
-                    Some(CompositeType::Func(_)) => choices.push(HT::NoFunc),
+                    Some(CompositeType::Func(_)) => choices.push(HT::Abstract {
+                        shared: false, // TODO: handle shared
+                        ty: AbstractHeapType::NoFunc,
+                    }),
                     None => {
                         // The referenced type might be part of this same rec
                         // group we are currently generating, but not generated
@@ -774,14 +805,6 @@ impl Module {
                     }
                 }
             }
-            HT::Func => {
-                choices.extend(self.func_types.iter().copied().map(HT::Concrete));
-                choices.push(HT::NoFunc);
-            }
-            HT::Extern => {
-                choices.push(HT::NoExtern);
-            }
-            HT::Exn | HT::NoExn | HT::None | HT::NoExtern | HT::NoFunc => {}
         }
         Ok(*u.choose(&choices)?)
     }
@@ -848,35 +871,58 @@ impl Module {
         use HeapType as HT;
         let mut choices = vec![ty];
         match ty {
-            HT::None => {
-                choices.extend([HT::Any, HT::Eq, HT::Struct, HT::Array, HT::I31]);
-                choices.extend(self.array_types.iter().copied().map(HT::Concrete));
-                choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
-            }
-            HT::NoExtern => {
-                choices.push(HT::Extern);
-            }
-            HT::NoFunc => {
-                choices.extend(self.func_types.iter().copied().map(HT::Concrete));
-                choices.push(HT::Func);
-            }
-            HT::NoExn => {
-                choices.push(HT::Exn);
+            HT::Abstract { shared, ty } => {
+                use AbstractHeapType::*;
+                let ht = |ty| HT::Abstract { shared, ty };
+                match ty {
+                    None => {
+                        choices.extend([ht(Any), ht(Eq), ht(Struct), ht(Array), ht(I31)]);
+                        choices.extend(self.array_types.iter().copied().map(HT::Concrete));
+                        choices.extend(self.struct_types.iter().copied().map(HT::Concrete));
+                    }
+                    NoExtern => {
+                        choices.push(ht(Extern));
+                    }
+                    NoFunc => {
+                        choices.extend(self.func_types.iter().copied().map(HT::Concrete));
+                        choices.push(ht(Func));
+                    }
+                    NoExn => {
+                        choices.push(ht(Exn));
+                    }
+                    Struct | Array | I31 => {
+                        choices.extend([ht(Any), ht(Eq)]);
+                    }
+                    Eq => {
+                        choices.push(ht(Any));
+                    }
+                    Exn | Any | Func | Extern => {}
+                }
             }
             HT::Concrete(mut idx) => {
+                // TODO: handle shared
+                let ht = |ty| HT::Abstract { shared: false, ty };
                 match &self
                     .types
                     .get(usize::try_from(idx).unwrap())
                     .map(|ty| &ty.composite_type)
                 {
                     Some(CompositeType::Array(_)) => {
-                        choices.extend([HT::Any, HT::Eq, HT::Array]);
+                        choices.extend([
+                            ht(AbstractHeapType::Any),
+                            ht(AbstractHeapType::Eq),
+                            ht(AbstractHeapType::Array),
+                        ]);
                     }
                     Some(CompositeType::Func(_)) => {
-                        choices.push(HT::Func);
+                        choices.push(ht(AbstractHeapType::Func));
                     }
                     Some(CompositeType::Struct(_)) => {
-                        choices.extend([HT::Any, HT::Eq, HT::Struct]);
+                        choices.extend([
+                            ht(AbstractHeapType::Any),
+                            ht(AbstractHeapType::Eq),
+                            ht(AbstractHeapType::Struct),
+                        ]);
                     }
                     None => {
                         // Same as in `arbitrary_matching_heap_type`: this was a
@@ -896,13 +942,6 @@ impl Module {
                     idx = supertype;
                 }
             }
-            HT::Struct | HT::Array | HT::I31 => {
-                choices.extend([HT::Any, HT::Eq]);
-            }
-            HT::Eq => {
-                choices.push(HT::Any);
-            }
-            HT::Exn | HT::Any | HT::Func | HT::Extern => {}
         }
         Ok(*u.choose(&choices)?)
     }
@@ -972,27 +1011,23 @@ impl Module {
             return Ok(HeapType::Concrete(idx));
         }
 
-        let mut choices = vec![HeapType::Func, HeapType::Extern];
+        use AbstractHeapType::*;
+        let mut choices = vec![Func, Extern];
         if self.config.exceptions_enabled {
-            choices.push(HeapType::Exn);
+            choices.push(Exn);
         }
         if self.config.gc_enabled {
             choices.extend(
-                [
-                    HeapType::Any,
-                    HeapType::None,
-                    HeapType::NoExtern,
-                    HeapType::NoFunc,
-                    HeapType::Eq,
-                    HeapType::Struct,
-                    HeapType::Array,
-                    HeapType::I31,
-                ]
-                .iter()
-                .copied(),
+                [Any, None, NoExtern, NoFunc, Eq, Struct, Array, I31]
+                    .iter()
+                    .copied(),
             );
         }
-        u.choose(&choices).copied()
+
+        Ok(HeapType::Abstract {
+            shared: false, // TODO: turn on shared attribute with shared-everything-threads.
+            ty: *u.choose(&choices)?,
+        })
     }
 
     fn arbitrary_func_type(&mut self, u: &mut Unstructured) -> Result<Rc<FuncType>> {
@@ -1572,7 +1607,10 @@ impl Module {
                 }
 
                 match ty.heap_type {
-                    HeapType::Func if num_funcs > 0 => {
+                    HeapType::Abstract {
+                        ty: AbstractHeapType::Func,
+                        ..
+                    } if num_funcs > 0 => {
                         choices.push(Box::new(move |u, _| {
                             let func = u.int_in_range(0..=num_funcs - 1)?;
                             Ok(ConstExpr::ref_func(func))
@@ -1636,22 +1674,31 @@ impl Module {
     #[cfg(feature = "wasmparser")]
     fn _required_exports(&mut self, u: &mut Unstructured, example_module: &[u8]) -> Result<()> {
         fn convert_heap_type(ty: &wasmparser::HeapType) -> HeapType {
+            use wasmparser::AbstractHeapType::*;
             match ty {
                 wasmparser::HeapType::Concrete(_) => {
                     panic!("Unable to handle concrete types in exports")
                 }
-                wasmparser::HeapType::Func => HeapType::Func,
-                wasmparser::HeapType::Extern => HeapType::Extern,
-                wasmparser::HeapType::Any => HeapType::Any,
-                wasmparser::HeapType::None => HeapType::None,
-                wasmparser::HeapType::NoExtern => HeapType::NoExtern,
-                wasmparser::HeapType::NoFunc => HeapType::NoFunc,
-                wasmparser::HeapType::Eq => HeapType::Eq,
-                wasmparser::HeapType::Struct => HeapType::Struct,
-                wasmparser::HeapType::Array => HeapType::Array,
-                wasmparser::HeapType::I31 => HeapType::I31,
-                wasmparser::HeapType::Exn => HeapType::Exn,
-                wasmparser::HeapType::NoExn => HeapType::NoExn,
+                wasmparser::HeapType::Abstract { shared, ty } => {
+                    let ty = match ty {
+                        Func => AbstractHeapType::Func,
+                        Extern => AbstractHeapType::Extern,
+                        Any => AbstractHeapType::Any,
+                        None => AbstractHeapType::None,
+                        NoExtern => AbstractHeapType::NoExtern,
+                        NoFunc => AbstractHeapType::NoFunc,
+                        Eq => AbstractHeapType::Eq,
+                        Struct => AbstractHeapType::Struct,
+                        Array => AbstractHeapType::Array,
+                        I31 => AbstractHeapType::I31,
+                        Exn => AbstractHeapType::Exn,
+                        NoExn => AbstractHeapType::NoExn,
+                    };
+                    HeapType::Abstract {
+                        shared: *shared,
+                        ty,
+                    }
+                }
             }
         }
 
@@ -2016,7 +2063,10 @@ impl Module {
                 let mut func_candidates = Vec::new();
                 if can_use_function_list {
                     match ty.heap_type {
-                        HeapType::Func => {
+                        HeapType::Abstract {
+                            ty: AbstractHeapType::Func,
+                            ..
+                        } => {
                             func_candidates.extend(0..self.funcs.len() as u32);
                         }
                         HeapType::Concrete(ty) => {
@@ -2450,21 +2500,14 @@ pub(crate) fn configured_valtypes(config: &Config) -> Vec<ValType> {
             // contain a non-null self-reference are also impossible to create).
             true,
         ] {
-            for heap_type in [
-                HeapType::Any,
-                HeapType::Eq,
-                HeapType::I31,
-                HeapType::Array,
-                HeapType::Struct,
-                HeapType::None,
-                HeapType::Func,
-                HeapType::NoFunc,
-                HeapType::Extern,
-                HeapType::NoExtern,
+            use AbstractHeapType::*;
+            for ty in [
+                Any, Eq, I31, Array, Struct, None, Func, NoFunc, Extern, NoExtern,
             ] {
                 valtypes.push(ValType::Ref(RefType {
                     nullable,
-                    heap_type,
+                    // TODO: handle shared
+                    heap_type: HeapType::Abstract { shared: false, ty },
                 }));
             }
         }
diff --git a/crates/wasm-smith/src/core/code_builder.rs b/crates/wasm-smith/src/core/code_builder.rs
index 0c67b6a9..d93ce0c0 100644
--- a/crates/wasm-smith/src/core/code_builder.rs
+++ b/crates/wasm-smith/src/core/code_builder.rs
@@ -7,8 +7,8 @@ use arbitrary::{Result, Unstructured};
 use std::collections::{BTreeMap, BTreeSet};
 use std::rc::Rc;
 use wasm_encoder::{
-    ArrayType, BlockType, Catch, ConstExpr, ExportKind, FieldType, GlobalType, HeapType, MemArg,
-    RefType, StorageType, StructType,
+    AbstractHeapType, ArrayType, BlockType, Catch, ConstExpr, ExportKind, FieldType, GlobalType,
+    HeapType, MemArg, RefType, StorageType, StructType,
 };
 mod no_traps;
 
@@ -2044,6 +2044,14 @@ fn call_ref(
 
 #[inline]
 fn call_indirect_valid(module: &Module, builder: &mut CodeBuilder) -> bool {
+    call_indirect_valid_impl(module, builder, false)
+}
+
+fn call_indirect_valid_impl(
+    module: &Module,
+    builder: &mut CodeBuilder,
+    is_return_call: bool,
+) -> bool {
     if module.config.disallow_traps {
         // We have no way to reflect, at run time, on a `funcref` in
         // the `i`th slot in a table and dynamically avoid trapping
@@ -2059,9 +2067,10 @@ fn call_indirect_valid(module: &Module, builder: &mut CodeBuilder) -> bool {
         return false;
     }
     let ty = builder.allocs.operands.pop().unwrap();
-    let is_valid = module
-        .func_types()
-        .any(|(_, ty)| builder.types_on_stack(module, &ty.params));
+    let is_valid = module.func_types().any(|(_, ty)| {
+        builder.types_on_stack(module, &ty.params)
+            && (!is_return_call || builder.allocs.controls[0].label_types() == &ty.results)
+    });
     builder.allocs.operands.push(ty);
     is_valid
 }
@@ -2197,8 +2206,7 @@ fn return_call_indirect_valid(module: &Module, builder: &mut CodeBuilder) -> boo
     if !module.config.tail_call_enabled {
         return false;
     }
-
-    call_indirect_valid(module, builder)
+    call_indirect_valid_impl(module, builder, true)
 }
 
 fn return_call_indirect(
@@ -2301,18 +2309,12 @@ fn br_on_null(
             if !module.types.is_empty() && u.arbitrary()? {
                 HeapType::Concrete(u.int_in_range(0..=u32::try_from(module.types.len()).unwrap())?)
             } else {
-                *u.choose(&[
-                    HeapType::Func,
-                    HeapType::Extern,
-                    HeapType::Any,
-                    HeapType::None,
-                    HeapType::NoExtern,
-                    HeapType::NoFunc,
-                    HeapType::Eq,
-                    HeapType::Struct,
-                    HeapType::Array,
-                    HeapType::I31,
-                ])?
+                use AbstractHeapType::*;
+                let ty = *u.choose(&[
+                    Func, Extern, Any, None, NoExtern, NoFunc, Eq, Struct, Array, I31,
+                ])?;
+                // TODO: handle shared
+                HeapType::Abstract { shared: false, ty }
             }
         }
     };
@@ -5384,18 +5386,23 @@ fn ref_null(
         choices.push(RefType::EXNREF);
     }
     if module.config.gc_enabled {
+        use AbstractHeapType::*;
         let r = |heap_type| RefType {
             nullable: true,
             heap_type,
         };
-        choices.push(r(HeapType::Any));
-        choices.push(r(HeapType::Eq));
-        choices.push(r(HeapType::Array));
-        choices.push(r(HeapType::Struct));
-        choices.push(r(HeapType::I31));
-        choices.push(r(HeapType::None));
-        choices.push(r(HeapType::NoFunc));
-        choices.push(r(HeapType::NoExtern));
+        let a = |abstract_heap_type| HeapType::Abstract {
+            shared: false, // TODO: handle shared
+            ty: abstract_heap_type,
+        };
+        choices.push(r(a(Any)));
+        choices.push(r(a(Eq)));
+        choices.push(r(a(Array)));
+        choices.push(r(a(Struct)));
+        choices.push(r(a(I31)));
+        choices.push(r(a(None)));
+        choices.push(r(a(NoFunc)));
+        choices.push(r(a(NoExtern)));
         for i in 0..module.types.len() {
             let i = u32::try_from(i).unwrap();
             choices.push(r(HeapType::Concrete(i)));
@@ -5457,10 +5464,7 @@ fn ref_as_non_null(
 
 #[inline]
 fn ref_eq_valid(module: &Module, builder: &mut CodeBuilder) -> bool {
-    let eq_ref = ValType::Ref(RefType {
-        nullable: true,
-        heap_type: HeapType::Eq,
-    });
+    let eq_ref = ValType::Ref(RefType::EQREF);
     module.config.gc_enabled && builder.types_on_stack(module, &[eq_ref, eq_ref])
 }
 
@@ -6359,14 +6363,7 @@ fn array_set(
 
 #[inline]
 fn array_len_valid(module: &Module, builder: &mut CodeBuilder) -> bool {
-    module.config.gc_enabled
-        && builder.type_on_stack(
-            module,
-            ValType::Ref(RefType {
-                nullable: true,
-                heap_type: HeapType::Array,
-            }),
-        )
+    module.config.gc_enabled && builder.type_on_stack(module, ValType::Ref(RefType::ARRAYREF))
 }
 
 fn array_len(
@@ -6574,10 +6571,7 @@ fn ref_i31(
     instructions: &mut Vec<Instruction>,
 ) -> Result<()> {
     builder.pop_operand();
-    builder.push_operand(Some(ValType::Ref(RefType {
-        nullable: false,
-        heap_type: HeapType::I31,
-    })));
+    builder.push_operand(Some(ValType::Ref(RefType::I31REF)));
     instructions.push(Instruction::RefI31);
     Ok(())
 }
@@ -6617,7 +6611,7 @@ fn any_convert_extern_valid(module: &Module, builder: &mut CodeBuilder) -> bool
             module,
             ValType::Ref(RefType {
                 nullable: true,
-                heap_type: HeapType::Extern,
+                heap_type: HeapType::EXTERN,
             }),
         )
 }
@@ -6634,7 +6628,7 @@ fn any_convert_extern(
     };
     builder.push_operand(Some(ValType::Ref(RefType {
         nullable,
-        heap_type: HeapType::Any,
+        heap_type: HeapType::ANY,
     })));
     instructions.push(Instruction::AnyConvertExtern);
     Ok(())
@@ -6642,14 +6636,7 @@ fn any_convert_extern(
 
 #[inline]
 fn extern_convert_any_valid(module: &Module, builder: &mut CodeBuilder) -> bool {
-    module.config.gc_enabled
-        && builder.type_on_stack(
-            module,
-            ValType::Ref(RefType {
-                nullable: true,
-                heap_type: HeapType::Any,
-            }),
-        )
+    module.config.gc_enabled && builder.type_on_stack(module, ValType::Ref(RefType::ANYREF))
 }
 
 fn extern_convert_any(
@@ -6662,10 +6649,12 @@ fn extern_convert_any(
         None => u.arbitrary()?,
         Some(r) => r.nullable,
     };
-    builder.push_operand(Some(ValType::Ref(RefType {
-        nullable,
-        heap_type: HeapType::Extern,
-    })));
+    let ty = if nullable {
+        RefType::EXTERNREF.nullable(true)
+    } else {
+        RefType::EXTERNREF
+    };
+    builder.push_operand(Some(ValType::Ref(ty)));
     instructions.push(Instruction::ExternConvertAny);
     Ok(())
 }
diff --git a/crates/wasmparser/src/readers/core/types.rs b/crates/wasmparser/src/readers/core/types.rs
index 8df5c03d..eb1b253d 100644
--- a/crates/wasmparser/src/readers/core/types.rs
+++ b/crates/wasmparser/src/readers/core/types.rs
@@ -570,6 +570,11 @@ impl CompositeType {
             _ => panic!("not a struct"),
         }
     }
+
+    /// Is the composite type `shared`?
+    pub fn is_shared(&self) -> bool {
+        todo!("shared composite types are not yet implemented")
+    }
 }
 
 /// Represents a type of a function in a WebAssembly module.
@@ -818,15 +823,6 @@ impl ValType {
         }
     }
 
-    /// Whether the type is `shared`.
-    pub fn is_shared(&self) -> bool {
-        match *self {
-            Self::I32 | Self::I64 | Self::F32 | Self::F64 | Self::V128 => true,
-            // TODO: parsing of `shared` refs is not yet implemented.
-            Self::Ref(_) => false,
-        }
-    }
-
     /// Maps any `UnpackedIndex` via the specified closure.
     #[cfg(feature = "validate")]
     pub(crate) fn remap_indices(
@@ -857,27 +853,29 @@ impl ValType {
 /// The GC proposal introduces heap types: any, eq, i31, struct, array,
 /// nofunc, noextern, none.
 //
-// RefType is a bit-packed enum that fits in a `u24` aka `[u8; 3]`.
-// Note that its content is opaque (and subject to change), but its API
-// is stable.
+// RefType is a bit-packed enum that fits in a `u24` aka `[u8; 3]`. Note that
+// its content is opaque (and subject to change), but its API is stable.
 //
 // It has the following internal structure:
 //
 // ```
 // [nullable:u1 concrete==1:u1 index:u22]
-// [nullable:u1 concrete==0:u1 abstype:u4 (unused):u18]
+// [nullable:u1 concrete==0:u1 shared:u1 abstype:u4 (unused):u17]
 // ```
 //
 // Where
 //
 // - `nullable` determines nullability of the ref,
 //
-// - `concrete` determines if the ref is of a dynamically defined type
-//   with an index (encoded in a following bit-packing section) or of a
-//   known fixed type,
+// - `concrete` determines if the ref is of a dynamically defined type with an
+//   index (encoded in a following bit-packing section) or of a known fixed
+//   type,
 //
 // - `index` is the type index,
 //
+// - `shared` determines if the ref is shared, but only if it is not concrete in
+//   which case we would need to examine the type at the concrete index,
+//
 // - `abstype` is an enumeration of abstract types:
 //
 //   ```
@@ -903,33 +901,31 @@ pub struct RefType([u8; 3]);
 
 impl fmt::Debug for RefType {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match (self.is_nullable(), self.heap_type()) {
-            (true, HeapType::Any) => write!(f, "anyref"),
-            (false, HeapType::Any) => write!(f, "(ref any)"),
-            (true, HeapType::None) => write!(f, "nullref"),
-            (false, HeapType::None) => write!(f, "(ref none)"),
-            (true, HeapType::NoExtern) => write!(f, "nullexternref"),
-            (false, HeapType::NoExtern) => write!(f, "(ref noextern)"),
-            (true, HeapType::NoFunc) => write!(f, "nullfuncref"),
-            (false, HeapType::NoFunc) => write!(f, "(ref nofunc)"),
-            (true, HeapType::Eq) => write!(f, "eqref"),
-            (false, HeapType::Eq) => write!(f, "(ref eq)"),
-            (true, HeapType::Struct) => write!(f, "structref"),
-            (false, HeapType::Struct) => write!(f, "(ref struct)"),
-            (true, HeapType::Array) => write!(f, "arrayref"),
-            (false, HeapType::Array) => write!(f, "(ref array)"),
-            (true, HeapType::I31) => write!(f, "i31ref"),
-            (false, HeapType::I31) => write!(f, "(ref i31)"),
-            (true, HeapType::Extern) => write!(f, "externref"),
-            (false, HeapType::Extern) => write!(f, "(ref extern)"),
-            (true, HeapType::Func) => write!(f, "funcref"),
-            (false, HeapType::Func) => write!(f, "(ref func)"),
-            (true, HeapType::Exn) => write!(f, "exnref"),
-            (false, HeapType::Exn) => write!(f, "(ref exn)"),
-            (true, HeapType::NoExn) => write!(f, "nullexnref"),
-            (false, HeapType::NoExn) => write!(f, "(ref noexn)"),
-            (true, HeapType::Concrete(idx)) => write!(f, "(ref null {idx})"),
-            (false, HeapType::Concrete(idx)) => write!(f, "(ref {idx})"),
+        let heap_type = self.heap_type();
+        match heap_type {
+            // All abstract types follow the same general pattern.
+            HeapType::Abstract { shared, ty } => {
+                let nullable = self.is_nullable();
+
+                let name = ty.as_str(nullable);
+                match (nullable, shared) {
+                    // Print the shortened form if nullable; i.e., `*ref`.
+                    (true, true) => write!(f, "(shared {}ref)", name),
+                    (true, false) => write!(f, "{}ref", name),
+                    // Print the long form otherwise; i.e., `(ref *)`.
+                    (false, true) => write!(f, "(ref (shared {}))", name),
+                    (false, false) => write!(f, "(ref {})", name),
+                }
+            }
+            // Handle concrete types separately; they always use the long form
+            // and don't show `shared`-ness.
+            HeapType::Concrete(index) => {
+                if self.is_nullable() {
+                    write!(f, "(ref null {})", index)
+                } else {
+                    write!(f, "(ref {})", index)
+                }
+            }
         }
     }
 }
@@ -967,19 +963,20 @@ impl RefType {
     const CONCRETE_BIT: u32 = 1 << 22;
 
     // The `abstype` field is valid only when `concrete == 0`.
-    const ABSTYPE_MASK: u32 = 0b1111 << 18;
-    const ANY_ABSTYPE: u32 = 0b1111 << 18;
-    const EQ_ABSTYPE: u32 = 0b1101 << 18;
-    const I31_ABSTYPE: u32 = 0b1000 << 18;
-    const STRUCT_ABSTYPE: u32 = 0b1001 << 18;
-    const ARRAY_ABSTYPE: u32 = 0b1100 << 18;
-    const FUNC_ABSTYPE: u32 = 0b0101 << 18;
-    const NOFUNC_ABSTYPE: u32 = 0b0100 << 18;
-    const EXTERN_ABSTYPE: u32 = 0b0011 << 18;
-    const NOEXTERN_ABSTYPE: u32 = 0b0010 << 18;
-    const EXN_ABSTYPE: u32 = 0b0001 << 18;
-    const NOEXN_ABSTYPE: u32 = 0b1110 << 18;
-    const NONE_ABSTYPE: u32 = 0b0000 << 18;
+    const SHARED_BIT: u32 = 1 << 21;
+    const ABSTYPE_MASK: u32 = 0b1111 << 17;
+    const ANY_ABSTYPE: u32 = 0b1111 << 17;
+    const EQ_ABSTYPE: u32 = 0b1101 << 17;
+    const I31_ABSTYPE: u32 = 0b1000 << 17;
+    const STRUCT_ABSTYPE: u32 = 0b1001 << 17;
+    const ARRAY_ABSTYPE: u32 = 0b1100 << 17;
+    const FUNC_ABSTYPE: u32 = 0b0101 << 17;
+    const NOFUNC_ABSTYPE: u32 = 0b0100 << 17;
+    const EXTERN_ABSTYPE: u32 = 0b0011 << 17;
+    const NOEXTERN_ABSTYPE: u32 = 0b0010 << 17;
+    const EXN_ABSTYPE: u32 = 0b0001 << 17;
+    const NOEXN_ABSTYPE: u32 = 0b1110 << 17;
+    const NONE_ABSTYPE: u32 = 0b0000 << 17;
 
     // The `index` is valid only when `concrete == 1`.
     const INDEX_MASK: u32 = (1 << 22) - 1;
@@ -1127,21 +1124,27 @@ impl RefType {
     /// Returns `None` when the heap type's type index (if any) is beyond this
     /// crate's implementation limits and therefore is not representable.
     pub fn new(nullable: bool, heap_type: HeapType) -> Option<Self> {
-        let nullable32 = Self::NULLABLE_BIT * (nullable as u32);
+        let base32 = Self::NULLABLE_BIT * (nullable as u32);
         match heap_type {
             HeapType::Concrete(index) => Some(RefType::concrete(nullable, index.pack()?)),
-            HeapType::Func => Some(Self::from_u32(nullable32 | Self::FUNC_ABSTYPE)),
-            HeapType::Extern => Some(Self::from_u32(nullable32 | Self::EXTERN_ABSTYPE)),
-            HeapType::Any => Some(Self::from_u32(nullable32 | Self::ANY_ABSTYPE)),
-            HeapType::None => Some(Self::from_u32(nullable32 | Self::NONE_ABSTYPE)),
-            HeapType::NoExtern => Some(Self::from_u32(nullable32 | Self::NOEXTERN_ABSTYPE)),
-            HeapType::NoFunc => Some(Self::from_u32(nullable32 | Self::NOFUNC_ABSTYPE)),
-            HeapType::Eq => Some(Self::from_u32(nullable32 | Self::EQ_ABSTYPE)),
-            HeapType::Struct => Some(Self::from_u32(nullable32 | Self::STRUCT_ABSTYPE)),
-            HeapType::Array => Some(Self::from_u32(nullable32 | Self::ARRAY_ABSTYPE)),
-            HeapType::I31 => Some(Self::from_u32(nullable32 | Self::I31_ABSTYPE)),
-            HeapType::Exn => Some(Self::from_u32(nullable32 | Self::EXN_ABSTYPE)),
-            HeapType::NoExn => Some(Self::from_u32(nullable32 | Self::NOEXN_ABSTYPE)),
+            HeapType::Abstract { shared, ty } => {
+                use AbstractHeapType::*;
+                let base32 = base32 | (Self::SHARED_BIT * (shared as u32));
+                match ty {
+                    Func => Some(Self::from_u32(base32 | Self::FUNC_ABSTYPE)),
+                    Extern => Some(Self::from_u32(base32 | Self::EXTERN_ABSTYPE)),
+                    Any => Some(Self::from_u32(base32 | Self::ANY_ABSTYPE)),
+                    None => Some(Self::from_u32(base32 | Self::NONE_ABSTYPE)),
+                    NoExtern => Some(Self::from_u32(base32 | Self::NOEXTERN_ABSTYPE)),
+                    NoFunc => Some(Self::from_u32(base32 | Self::NOFUNC_ABSTYPE)),
+                    Eq => Some(Self::from_u32(base32 | Self::EQ_ABSTYPE)),
+                    Struct => Some(Self::from_u32(base32 | Self::STRUCT_ABSTYPE)),
+                    Array => Some(Self::from_u32(base32 | Self::ARRAY_ABSTYPE)),
+                    I31 => Some(Self::from_u32(base32 | Self::I31_ABSTYPE)),
+                    Exn => Some(Self::from_u32(base32 | Self::EXN_ABSTYPE)),
+                    NoExn => Some(Self::from_u32(base32 | Self::NOEXN_ABSTYPE)),
+                }
+            }
         }
     }
 
@@ -1193,7 +1196,7 @@ impl RefType {
         !self.is_concrete_type_ref() && self.abstype() == Self::EXTERN_ABSTYPE
     }
 
-    /// Is this the abstract untyped array refrence type aka `(ref null
+    /// Is this the abstract untyped array reference type aka `(ref null
     /// array)` aka `arrayref`?
     pub const fn is_array_ref(&self) -> bool {
         !self.is_concrete_type_ref() && self.abstype() == Self::ARRAY_ABSTYPE
@@ -1220,27 +1223,39 @@ impl RefType {
         Self::from_u32(self.as_u32() | Self::NULLABLE_BIT)
     }
 
+    /// Get the shared version of this ref type as long as it is abstract.
+    pub const fn shared(&self) -> Option<Self> {
+        if self.is_concrete_type_ref() {
+            None
+        } else {
+            Some(Self::from_u32(self.as_u32() | Self::SHARED_BIT))
+        }
+    }
+
     /// Get the heap type that this is a reference to.
     pub fn heap_type(&self) -> HeapType {
         let s = self.as_u32();
         if self.is_concrete_type_ref() {
             HeapType::Concrete(self.type_index().unwrap().unpack())
         } else {
-            match s & Self::ABSTYPE_MASK {
-                Self::FUNC_ABSTYPE => HeapType::Func,
-                Self::EXTERN_ABSTYPE => HeapType::Extern,
-                Self::ANY_ABSTYPE => HeapType::Any,
-                Self::NONE_ABSTYPE => HeapType::None,
-                Self::NOEXTERN_ABSTYPE => HeapType::NoExtern,
-                Self::NOFUNC_ABSTYPE => HeapType::NoFunc,
-                Self::EQ_ABSTYPE => HeapType::Eq,
-                Self::STRUCT_ABSTYPE => HeapType::Struct,
-                Self::ARRAY_ABSTYPE => HeapType::Array,
-                Self::I31_ABSTYPE => HeapType::I31,
-                Self::EXN_ABSTYPE => HeapType::Exn,
-                Self::NOEXN_ABSTYPE => HeapType::NoExn,
+            use AbstractHeapType::*;
+            let shared = s & Self::SHARED_BIT != 0;
+            let ty = match s & Self::ABSTYPE_MASK {
+                Self::FUNC_ABSTYPE => Func,
+                Self::EXTERN_ABSTYPE => Extern,
+                Self::ANY_ABSTYPE => Any,
+                Self::NONE_ABSTYPE => None,
+                Self::NOEXTERN_ABSTYPE => NoExtern,
+                Self::NOFUNC_ABSTYPE => NoFunc,
+                Self::EQ_ABSTYPE => Eq,
+                Self::STRUCT_ABSTYPE => Struct,
+                Self::ARRAY_ABSTYPE => Array,
+                Self::I31_ABSTYPE => I31,
+                Self::EXN_ABSTYPE => Exn,
+                Self::NOEXN_ABSTYPE => NoExn,
                 _ => unreachable!(),
-            }
+            };
+            HeapType::Abstract { shared, ty }
         }
     }
 
@@ -1248,33 +1263,72 @@ impl RefType {
     // the indexes stubbed out.
     #[cfg(feature = "validate")]
     pub(crate) fn wat(&self) -> &'static str {
-        match (self.is_nullable(), self.heap_type()) {
-            (true, HeapType::Func) => "funcref",
-            (true, HeapType::Extern) => "externref",
-            (true, HeapType::Concrete(_)) => "(ref null $type)",
-            (true, HeapType::Any) => "anyref",
-            (true, HeapType::None) => "nullref",
-            (true, HeapType::NoExtern) => "nullexternref",
-            (true, HeapType::NoFunc) => "nullfuncref",
-            (true, HeapType::Eq) => "eqref",
-            (true, HeapType::Struct) => "structref",
-            (true, HeapType::Array) => "arrayref",
-            (true, HeapType::I31) => "i31ref",
-            (true, HeapType::Exn) => "exnref",
-            (true, HeapType::NoExn) => "nullexnref",
-            (false, HeapType::Func) => "(ref func)",
-            (false, HeapType::Extern) => "(ref extern)",
-            (false, HeapType::Concrete(_)) => "(ref $type)",
-            (false, HeapType::Any) => "(ref any)",
-            (false, HeapType::None) => "(ref none)",
-            (false, HeapType::NoExtern) => "(ref noextern)",
-            (false, HeapType::NoFunc) => "(ref nofunc)",
-            (false, HeapType::Eq) => "(ref eq)",
-            (false, HeapType::Struct) => "(ref struct)",
-            (false, HeapType::Array) => "(ref array)",
-            (false, HeapType::I31) => "(ref i31)",
-            (false, HeapType::Exn) => "(ref exn)",
-            (false, HeapType::NoExn) => "(ref noexn)",
+        let nullable = self.is_nullable();
+        match self.heap_type() {
+            HeapType::Abstract { shared, ty } => {
+                use AbstractHeapType::*;
+                match (shared, nullable, ty) {
+                    // Shared and nullable.
+                    (true, true, Func) => "(shared funcref)",
+                    (true, true, Extern) => "(shared externref)",
+                    (true, true, Any) => "(shared anyref)",
+                    (true, true, None) => "(shared nullref)",
+                    (true, true, NoExtern) => "(shared nullexternref)",
+                    (true, true, NoFunc) => "(shared nullfuncref)",
+                    (true, true, Eq) => "(shared eqref)",
+                    (true, true, Struct) => "(shared structref)",
+                    (true, true, Array) => "(shared arrayref)",
+                    (true, true, I31) => "(shared i31ref)",
+                    (true, true, Exn) => "(shared exnref)",
+                    (true, true, NoExn) => "(shared nullexnref)",
+                    // Unshared but nullable.
+                    (false, true, Func) => "funcref",
+                    (false, true, Extern) => "externref",
+                    (false, true, Any) => "anyref",
+                    (false, true, None) => "nullref",
+                    (false, true, NoExtern) => "nullexternref",
+                    (false, true, NoFunc) => "nullfuncref",
+                    (false, true, Eq) => "eqref",
+                    (false, true, Struct) => "structref",
+                    (false, true, Array) => "arrayref",
+                    (false, true, I31) => "i31ref",
+                    (false, true, Exn) => "exnref",
+                    (false, true, NoExn) => "nullexnref",
+                    // Shared but not nullable.
+                    (true, false, Func) => "(ref (shared func))",
+                    (true, false, Extern) => "(ref (shared extern))",
+                    (true, false, Any) => "(ref (shared any))",
+                    (true, false, None) => "(ref (shared none))",
+                    (true, false, NoExtern) => "(ref (shared noextern))",
+                    (true, false, NoFunc) => "(ref (shared nofunc))",
+                    (true, false, Eq) => "(ref (shared eq))",
+                    (true, false, Struct) => "(ref (shared struct))",
+                    (true, false, Array) => "(ref (shared array))",
+                    (true, false, I31) => "(ref (shared i31))",
+                    (true, false, Exn) => "(ref (shared exn))",
+                    (true, false, NoExn) => "(ref (shared noexn))",
+                    // Neither shared nor nullable.
+                    (false, false, Func) => "(ref func)",
+                    (false, false, Extern) => "(ref extern)",
+                    (false, false, Any) => "(ref any)",
+                    (false, false, None) => "(ref none)",
+                    (false, false, NoExtern) => "(ref noextern)",
+                    (false, false, NoFunc) => "(ref nofunc)",
+                    (false, false, Eq) => "(ref eq)",
+                    (false, false, Struct) => "(ref struct)",
+                    (false, false, Array) => "(ref array)",
+                    (false, false, I31) => "(ref i31)",
+                    (false, false, Exn) => "(ref exn)",
+                    (false, false, NoExn) => "(ref noexn)",
+                }
+            }
+            HeapType::Concrete(_) => {
+                if nullable {
+                    "(ref null $type)"
+                } else {
+                    "(ref $type)"
+                }
+            }
         }
     }
 }
@@ -1282,11 +1336,38 @@ impl RefType {
 /// A heap type.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub enum HeapType {
+    /// An abstract heap type; e.g., `anyref`.
+    Abstract {
+        /// Whether the type is shared.
+        ///
+        /// Introduced in the shared-everything-threads proposal.
+        shared: bool,
+        /// The actual heap type.
+        ty: AbstractHeapType,
+    },
     /// A concrete, user-defined type.
     ///
     /// Introduced in the function-references proposal.
     Concrete(UnpackedIndex),
+}
 
+impl HeapType {
+    /// Alias for an unshared `func` heap type.
+    pub const FUNC: Self = Self::Abstract {
+        shared: false,
+        ty: AbstractHeapType::Func,
+    };
+
+    /// Alias for an unshared `extern` heap type.
+    pub const EXTERN: Self = Self::Abstract {
+        shared: false,
+        ty: AbstractHeapType::Extern,
+    };
+}
+
+/// An abstract heap type.
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub enum AbstractHeapType {
     /// The abstract, untyped (any) function.
     ///
     /// Introduced in the references-types proposal.
@@ -1369,6 +1450,30 @@ pub enum HeapType {
     NoExn,
 }
 
+impl AbstractHeapType {
+    const fn as_str(&self, nullable: bool) -> &str {
+        use AbstractHeapType::*;
+        match (nullable, self) {
+            (_, Any) => "any",
+            (true, None) => "null",
+            (false, None) => "none",
+            (true, NoExtern) => "nullextern",
+            (false, NoExtern) => "noextern",
+            (true, NoFunc) => "nullfunc",
+            (false, NoFunc) => "nofunc",
+            (_, Eq) => "eq",
+            (_, Struct) => "struct",
+            (_, Array) => "array",
+            (_, I31) => "i31",
+            (_, Extern) => "extern",
+            (_, Func) => "func",
+            (_, Exn) => "exn",
+            (true, NoExn) => "nullexn",
+            (false, NoExn) => "noexn",
+        }
+    }
+}
+
 impl<'a> FromReader<'a> for StorageType {
     fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
         // NB: See `FromReader<'a> for ValType` for a table of how this
@@ -1421,6 +1526,7 @@ impl<'a> FromReader<'a> for ValType {
         // | 0x6B    | -21     | struct       | gc proposal                  |
         // | 0x6A    | -22     | array        | gc proposal                  |
         // | 0x69    | -23     | exnref       | gc + exceptions proposal     |
+        // | 0x65    | -27     | shared $t    | shared-everything proposal   |
         // | 0x64    | -28     | ref $t       | gc proposal, prefix byte     |
         // | 0x63    | -29     | ref null $t  | gc proposal, prefix byte     |
         // | 0x60    | -32     | func $t      | prefix byte                  |
@@ -1457,8 +1563,8 @@ impl<'a> FromReader<'a> for ValType {
                 reader.read_u8()?;
                 Ok(ValType::V128)
             }
-            0x70 | 0x6F | 0x64 | 0x63 | 0x6E | 0x71 | 0x72 | 0x73 | 0x74 | 0x6D | 0x6B | 0x6A
-            | 0x6C | 0x69 => Ok(ValType::Ref(reader.read()?)),
+            0x70 | 0x6F | 0x65 | 0x64 | 0x63 | 0x6E | 0x71 | 0x72 | 0x73 | 0x74 | 0x6D | 0x6B
+            | 0x6A | 0x6C | 0x69 => Ok(ValType::Ref(reader.read()?)),
             _ => bail!(reader.original_position(), "invalid value type"),
         }
     }
@@ -1466,27 +1572,41 @@ impl<'a> FromReader<'a> for ValType {
 
 impl<'a> FromReader<'a> for RefType {
     fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
+        let absheapty = |byte, pos| match byte {
+            0x70 => Ok(RefType::FUNC.nullable()),
+            0x6F => Ok(RefType::EXTERN.nullable()),
+            0x6E => Ok(RefType::ANY.nullable()),
+            0x71 => Ok(RefType::NONE.nullable()),
+            0x72 => Ok(RefType::NOEXTERN.nullable()),
+            0x73 => Ok(RefType::NOFUNC.nullable()),
+            0x6D => Ok(RefType::EQ.nullable()),
+            0x6B => Ok(RefType::STRUCT.nullable()),
+            0x6A => Ok(RefType::ARRAY.nullable()),
+            0x6C => Ok(RefType::I31.nullable()),
+            0x69 => Ok(RefType::EXN.nullable()),
+            0x74 => Ok(RefType::NOEXN.nullable()),
+            _ => bail!(pos, "invalid abstract heap type"),
+        };
+
         // NB: See `FromReader<'a> for ValType` for a table of how this
         // interacts with other value encodings.
         match reader.read()? {
+            byte @ (0x70 | 0x6F | 0x6E | 0x71 | 0x72 | 0x73 | 0x6D | 0x6B | 0x6A | 0x6C | 0x69
+            | 0x74) => {
+                let pos = reader.original_position();
+                absheapty(byte, pos)
+            }
+            0x65 => {
+                let byte = reader.read()?;
+                let pos = reader.original_position();
+                Ok(absheapty(byte, pos)?.shared().expect("must be abstract"))
+            }
             byte @ (0x63 | 0x64) => {
                 let nullable = byte == 0x63;
                 let pos = reader.original_position();
                 RefType::new(nullable, reader.read()?)
                     .ok_or_else(|| crate::BinaryReaderError::new("type index too large", pos))
             }
-            0x69 => Ok(RefType::EXN.nullable()),
-            0x6A => Ok(RefType::ARRAY.nullable()),
-            0x6B => Ok(RefType::STRUCT.nullable()),
-            0x6C => Ok(RefType::I31.nullable()),
-            0x6D => Ok(RefType::EQ.nullable()),
-            0x6E => Ok(RefType::ANY.nullable()),
-            0x6F => Ok(RefType::EXTERN.nullable()),
-            0x70 => Ok(RefType::FUNC.nullable()),
-            0x71 => Ok(RefType::NONE.nullable()),
-            0x72 => Ok(RefType::NOEXTERN.nullable()),
-            0x73 => Ok(RefType::NOFUNC.nullable()),
-            0x74 => Ok(RefType::NOEXN.nullable()),
             _ => bail!(reader.original_position(), "malformed reference type"),
         }
     }
@@ -1497,53 +1617,14 @@ impl<'a> FromReader<'a> for HeapType {
         // NB: See `FromReader<'a> for ValType` for a table of how this
         // interacts with other value encodings.
         match reader.peek()? {
-            0x70 => {
-                reader.read_u8()?;
-                Ok(HeapType::Func)
-            }
-            0x6F => {
+            0x65 => {
                 reader.read_u8()?;
-                Ok(HeapType::Extern)
+                let ty = reader.read()?;
+                Ok(HeapType::Abstract { shared: true, ty })
             }
-            0x6E => {
-                reader.read_u8()?;
-                Ok(HeapType::Any)
-            }
-            0x71 => {
-                reader.read_u8()?;
-                Ok(HeapType::None)
-            }
-            0x72 => {
-                reader.read_u8()?;
-                Ok(HeapType::NoExtern)
-            }
-            0x73 => {
-                reader.read_u8()?;
-                Ok(HeapType::NoFunc)
-            }
-            0x6D => {
-                reader.read_u8()?;
-                Ok(HeapType::Eq)
-            }
-            0x6B => {
-                reader.read_u8()?;
-                Ok(HeapType::Struct)
-            }
-            0x6A => {
-                reader.read_u8()?;
-                Ok(HeapType::Array)
-            }
-            0x6C => {
-                reader.read_u8()?;
-                Ok(HeapType::I31)
-            }
-            0x69 => {
-                reader.read_u8()?;
-                Ok(HeapType::Exn)
-            }
-            0x74 => {
-                reader.read_u8()?;
-                Ok(HeapType::NoExn)
+            0x70 | 0x6F | 0x6E | 0x71 | 0x72 | 0x73 | 0x6D | 0x6B | 0x6A | 0x6C | 0x69 | 0x74 => {
+                let ty = reader.read()?;
+                Ok(HeapType::Abstract { shared: false, ty })
             }
             _ => {
                 let idx = match u32::try_from(reader.read_var_s33()?) {
@@ -1564,6 +1645,29 @@ impl<'a> FromReader<'a> for HeapType {
     }
 }
 
+impl<'a> FromReader<'a> for AbstractHeapType {
+    fn from_reader(reader: &mut BinaryReader<'a>) -> Result<Self> {
+        use AbstractHeapType::*;
+        match reader.read_u8()? {
+            0x70 => Ok(Func),
+            0x6F => Ok(Extern),
+            0x6E => Ok(Any),
+            0x71 => Ok(None),
+            0x72 => Ok(NoExtern),
+            0x73 => Ok(NoFunc),
+            0x6D => Ok(Eq),
+            0x6B => Ok(Struct),
+            0x6A => Ok(Array),
+            0x6C => Ok(I31),
+            0x69 => Ok(Exn),
+            0x74 => Ok(NoExn),
+            _ => {
+                bail!(reader.original_position(), "invalid abstract heap type");
+            }
+        }
+    }
+}
+
 /// Represents a table's type.
 #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
 pub struct TableType {
diff --git a/crates/wasmparser/src/resources.rs b/crates/wasmparser/src/resources.rs
index 20ce7435..729fc285 100644
--- a/crates/wasmparser/src/resources.rs
+++ b/crates/wasmparser/src/resources.rs
@@ -161,7 +161,6 @@ where
     fn is_subtype(&self, a: ValType, b: ValType) -> bool {
         T::is_subtype(self, a, b)
     }
-
     fn element_count(&self) -> u32 {
         T::element_count(self)
     }
diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs
index 5902b925..4396cb51 100644
--- a/crates/wasmparser/src/validator.rs
+++ b/crates/wasmparser/src/validator.rs
@@ -15,9 +15,9 @@
 
 use crate::prelude::*;
 use crate::{
-    limits::*, BinaryReaderError, Encoding, FromReader, FunctionBody, HeapType, Parser, Payload,
-    RefType, Result, SectionLimited, ValType, WasmFeatures, WASM_COMPONENT_VERSION,
-    WASM_MODULE_VERSION,
+    limits::*, AbstractHeapType, BinaryReaderError, Encoding, FromReader, FunctionBody, HeapType,
+    Parser, Payload, RefType, Result, SectionLimited, ValType, WasmFeatures,
+    WASM_COMPONENT_VERSION, WASM_MODULE_VERSION,
 };
 use ::core::mem;
 use ::core::ops::Range;
@@ -249,55 +249,54 @@ impl WasmFeatures {
         if !self.reference_types() {
             return Err("reference types support is not enabled");
         }
-        match (r.heap_type(), r.is_nullable()) {
-            // funcref/externref only require `reference-types`.
-            (HeapType::Func, true) | (HeapType::Extern, true) => Ok(()),
-
-            // Non-nullable func/extern references requires the
-            // `function-references` proposal.
-            (HeapType::Func | HeapType::Extern, false) => {
-                if self.function_references() {
-                    Ok(())
-                } else {
-                    Err("function references required for non-nullable types")
-                }
-            }
-
-            // Indexed types require either the function-references or gc
-            // proposal as gc implies function references here.
-            (HeapType::Concrete(_), _) => {
+        match r.heap_type() {
+            HeapType::Concrete(_) => {
+                // Indexed types require either the function-references or gc
+                // proposal as gc implies function references here.
                 if self.function_references() || self.gc() {
                     Ok(())
                 } else {
                     Err("function references required for index reference types")
                 }
             }
-
-            // These types were added in the gc proposal.
-            (
-                HeapType::Any
-                | HeapType::None
-                | HeapType::Eq
-                | HeapType::Struct
-                | HeapType::Array
-                | HeapType::I31
-                | HeapType::NoExtern
-                | HeapType::NoFunc,
-                _,
-            ) => {
-                if self.gc() {
-                    Ok(())
-                } else {
-                    Err("heap types not supported without the gc feature")
+            HeapType::Abstract { shared, ty } => {
+                use AbstractHeapType::*;
+                if shared && !self.shared_everything_threads() {
+                    return Err(
+                        "shared reference types require the shared-everything-threads proposal",
+                    );
                 }
-            }
+                match (ty, r.is_nullable()) {
+                    // funcref/externref only require `reference-types`.
+                    (Func, true) | (Extern, true) => Ok(()),
+
+                    // Non-nullable func/extern references requires the
+                    // `function-references` proposal.
+                    (Func | Extern, false) => {
+                        if self.function_references() {
+                            Ok(())
+                        } else {
+                            Err("function references required for non-nullable types")
+                        }
+                    }
 
-            // These types were added in the exception-handling proposal.
-            (HeapType::Exn | HeapType::NoExn, _) => {
-                if self.exceptions() {
-                    Ok(())
-                } else {
-                    Err("exception refs not supported without the exception handling feature")
+                    // These types were added in the gc proposal.
+                    (Any | None | Eq | Struct | Array | I31 | NoExtern | NoFunc, _) => {
+                        if self.gc() {
+                            Ok(())
+                        } else {
+                            Err("heap types not supported without the gc feature")
+                        }
+                    }
+
+                    // These types were added in the exception-handling proposal.
+                    (Exn | NoExn, _) => {
+                        if self.exceptions() {
+                            Ok(())
+                        } else {
+                            Err("exception refs not supported without the exception handling feature")
+                        }
+                    }
                 }
             }
         }
diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs
index b189c419..173a74d8 100644
--- a/crates/wasmparser/src/validator/core.rs
+++ b/crates/wasmparser/src/validator/core.rs
@@ -132,7 +132,7 @@ impl ModuleState {
         offset: usize,
     ) -> Result<()> {
         self.module
-            .check_global_type(&mut global.ty, features, offset)?;
+            .check_global_type(&mut global.ty, features, types, offset)?;
         self.check_const_expr(&global.init_expr, global.ty.content_type, features, types)?;
         self.module.assert_mut().globals.push(global.ty);
         Ok(())
@@ -855,7 +855,7 @@ impl Module {
                 EntityType::Tag(self.types[t.func_type_idx as usize])
             }
             TypeRef::Global(t) => {
-                self.check_global_type(t, features, offset)?;
+                self.check_global_type(t, features, types, offset)?;
                 EntityType::Global(*t)
             }
         })
@@ -1017,20 +1017,9 @@ impl Module {
     }
 
     fn check_heap_type(&self, ty: &mut HeapType, offset: usize) -> Result<()> {
-        // Check that the heap type is valid
+        // Check that the heap type is valid.
         let type_index = match ty {
-            HeapType::Func
-            | HeapType::Extern
-            | HeapType::Any
-            | HeapType::None
-            | HeapType::NoExtern
-            | HeapType::NoFunc
-            | HeapType::Eq
-            | HeapType::Struct
-            | HeapType::Array
-            | HeapType::I31
-            | HeapType::Exn
-            | HeapType::NoExn => return Ok(()),
+            HeapType::Abstract { .. } => return Ok(()),
             HeapType::Concrete(type_index) => type_index,
         };
         match type_index {
@@ -1073,6 +1062,7 @@ impl Module {
         &self,
         ty: &mut GlobalType,
         features: &WasmFeatures,
+        types: &TypeList,
         offset: usize,
     ) -> Result<()> {
         self.check_value_type(&mut ty.content_type, features, offset)?;
@@ -1083,7 +1073,7 @@ impl Module {
                     offset,
                 ));
             }
-            if !ty.content_type.is_shared() {
+            if !types.valtype_is_shared(ty.content_type) {
                 return Err(BinaryReaderError::new(
                     "shared globals must have a shared value type",
                     offset,
diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs
index 775964ce..e13994d3 100644
--- a/crates/wasmparser/src/validator/operators.rs
+++ b/crates/wasmparser/src/validator/operators.rs
@@ -22,13 +22,13 @@
 // confusing it's recommended to read over that section to see how it maps to
 // the various methods here.
 
+use crate::prelude::*;
 use crate::{
-    limits::MAX_WASM_FUNCTION_LOCALS, ArrayType, BinaryReaderError, BlockType, BrTable, Catch,
-    CompositeType, FieldType, FuncType, HeapType, Ieee32, Ieee64, MemArg, RefType, Result,
-    StorageType, StructType, SubType, TableType, TryTable, UnpackedIndex, ValType, VisitOperator,
-    WasmFeatures, WasmModuleResources, V128,
+    limits::MAX_WASM_FUNCTION_LOCALS, AbstractHeapType, ArrayType, BinaryReaderError, BlockType,
+    BrTable, Catch, CompositeType, FieldType, FuncType, GlobalType, HeapType, Ieee32, Ieee64,
+    MemArg, RefType, Result, StorageType, StructType, SubType, TableType, TryTable, UnpackedIndex,
+    ValType, VisitOperator, WasmFeatures, WasmModuleResources, V128,
 };
-use crate::{prelude::*, GlobalType};
 use core::ops::{Deref, DerefMut};
 
 pub(crate) struct OperatorValidator {
@@ -3991,7 +3991,11 @@ where
         let is_nullable = extern_ref
             .as_type()
             .map_or(false, |ty| ty.as_reference_type().unwrap().is_nullable());
-        let any_ref = RefType::new(is_nullable, HeapType::Any).unwrap();
+        let heap_type = HeapType::Abstract {
+            shared: false, // TODO: handle shared--see https://github.com/WebAssembly/shared-everything-threads/issues/65.
+            ty: AbstractHeapType::Any,
+        };
+        let any_ref = RefType::new(is_nullable, heap_type).unwrap();
         self.push_operand(any_ref)
     }
     fn visit_extern_convert_any(&mut self) -> Self::Output {
@@ -3999,7 +4003,11 @@ where
         let is_nullable = any_ref
             .as_type()
             .map_or(false, |ty| ty.as_reference_type().unwrap().is_nullable());
-        let extern_ref = RefType::new(is_nullable, HeapType::Extern).unwrap();
+        let heap_type = HeapType::Abstract {
+            shared: false, // TODO: handle shared--see https://github.com/WebAssembly/shared-everything-threads/issues/65.
+            ty: AbstractHeapType::Extern,
+        };
+        let extern_ref = RefType::new(is_nullable, heap_type).unwrap();
         self.push_operand(extern_ref)
     }
     fn visit_ref_test_non_null(&mut self, heap_type: HeapType) -> Self::Output {
diff --git a/crates/wasmparser/src/validator/types.rs b/crates/wasmparser/src/validator/types.rs
index 8da034d7..6e4f452e 100644
--- a/crates/wasmparser/src/validator/types.rs
+++ b/crates/wasmparser/src/validator/types.rs
@@ -4,8 +4,8 @@ use super::{
     component::{ComponentState, ExternKind},
     core::Module,
 };
-use crate::collections::map::Entry;
 use crate::prelude::*;
+use crate::{collections::map::Entry, AbstractHeapType};
 use crate::{validator::names::KebabString, HeapType, ValidatorId};
 use crate::{
     BinaryReaderError, CompositeType, Export, ExternalKind, FuncType, GlobalType, Import, Matches,
@@ -2743,63 +2743,87 @@ impl TypeList {
             &self[id]
         };
 
+        use AbstractHeapType::*;
         use HeapType as HT;
         match (a.heap_type(), b.heap_type()) {
             (a, b) if a == b => true,
 
-            (HT::Eq | HT::I31 | HT::Struct | HT::Array | HT::None, HT::Any) => true,
-            (HT::I31 | HT::Struct | HT::Array | HT::None, HT::Eq) => true,
-            (HT::NoExtern, HT::Extern) => true,
-            (HT::NoFunc, HT::Func) => true,
-            (HT::None, HT::I31 | HT::Array | HT::Struct) => true,
-
-            (HT::Concrete(a), HT::Eq | HT::Any) => matches!(
-                subtype(a_group, a).composite_type,
-                CompositeType::Array(_) | CompositeType::Struct(_)
-            ),
-
-            (HT::Concrete(a), HT::Struct) => {
-                matches!(subtype(a_group, a).composite_type, CompositeType::Struct(_))
+            (
+                HT::Abstract {
+                    shared: a_shared,
+                    ty: a_ty,
+                },
+                HT::Abstract {
+                    shared: b_shared,
+                    ty: b_ty,
+                },
+            ) => {
+                a_shared == b_shared
+                    && match (a_ty, b_ty) {
+                        (Eq | I31 | Struct | Array | None, Any) => true,
+                        (I31 | Struct | Array | None, Eq) => true,
+                        (NoExtern, Extern) => true,
+                        (NoFunc, Func) => true,
+                        (None, I31 | Array | Struct) => true,
+                        (NoExn, Exn) => true,
+                        // Nothing else matches. (Avoid full wildcard matches so
+                        // that adding/modifying variants is easier in the
+                        // future.)
+                        (
+                            Func | Extern | Exn | Any | Eq | Array | I31 | Struct | None | NoFunc
+                            | NoExtern | NoExn,
+                            _,
+                        ) => false,
+                    }
             }
 
-            (HT::Concrete(a), HT::Array) => {
-                matches!(subtype(a_group, a).composite_type, CompositeType::Array(_))
+            (HT::Concrete(a), HT::Abstract { shared, ty }) => {
+                if shared {
+                    // TODO: handle shared
+                    todo!("check shared-ness of concrete type");
+                }
+                match ty {
+                    Any | Eq => matches!(
+                        subtype(a_group, a).composite_type,
+                        CompositeType::Array(_) | CompositeType::Struct(_)
+                    ),
+                    Struct => {
+                        matches!(subtype(a_group, a).composite_type, CompositeType::Struct(_))
+                    }
+                    Array => {
+                        matches!(subtype(a_group, a).composite_type, CompositeType::Array(_))
+                    }
+                    Func => {
+                        matches!(subtype(a_group, a).composite_type, CompositeType::Func(_))
+                    }
+                    // Nothing else matches. (Avoid full wildcard matches so
+                    // that adding/modifying variants is easier in the future.)
+                    Extern | Exn | I31 | None | NoFunc | NoExtern | NoExn => false,
+                }
             }
 
-            (HT::Concrete(a), HT::Func) => {
-                matches!(subtype(a_group, a).composite_type, CompositeType::Func(_))
+            (HT::Abstract { shared, ty }, HT::Concrete(b)) => {
+                if shared {
+                    // TODO: handle shared
+                    todo!("check shared-ness of concrete type");
+                }
+                match ty {
+                    None => matches!(
+                        subtype(b_group, b).composite_type,
+                        CompositeType::Array(_) | CompositeType::Struct(_)
+                    ),
+                    NoFunc => matches!(subtype(b_group, b).composite_type, CompositeType::Func(_)),
+                    // Nothing else matches. (Avoid full wildcard matches so
+                    // that adding/modifying variants is easier in the future.)
+                    Func | Extern | Exn | Any | Eq | Array | I31 | Struct | NoExtern | NoExn => {
+                        false
+                    }
+                }
             }
 
             (HT::Concrete(a), HT::Concrete(b)) => {
                 self.id_is_subtype(core_type_id(a_group, a), core_type_id(b_group, b))
             }
-
-            (HT::None, HT::Concrete(b)) => matches!(
-                subtype(b_group, b).composite_type,
-                CompositeType::Array(_) | CompositeType::Struct(_)
-            ),
-
-            (HT::NoFunc, HT::Concrete(b)) => {
-                matches!(subtype(b_group, b).composite_type, CompositeType::Func(_))
-            }
-
-            (HT::NoExn, HT::Exn) => true,
-
-            // Nothing else matches. (Avoid full wildcard matches so that
-            // adding/modifying variants is easier in the future.)
-            (HT::Concrete(_), _)
-            | (HT::Func, _)
-            | (HT::Extern, _)
-            | (HT::Any, _)
-            | (HT::None, _)
-            | (HT::NoExtern, _)
-            | (HT::NoFunc, _)
-            | (HT::Eq, _)
-            | (HT::Struct, _)
-            | (HT::Array, _)
-            | (HT::I31, _)
-            | (HT::Exn, _)
-            | (HT::NoExn, _) => false,
         }
     }
 
@@ -2819,25 +2843,49 @@ impl TypeList {
         }
     }
 
+    /// Is `ty` shared?
+    ///
+    /// This is complicated by reference types, since they may have concrete
+    /// heap types whose shared-ness must be checked by looking at the type they
+    /// point to.
+    pub fn valtype_is_shared(&self, ty: ValType) -> bool {
+        match ty {
+            ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => true,
+            ValType::Ref(rt) => match rt.heap_type() {
+                HeapType::Abstract { shared, .. } => shared,
+                HeapType::Concrete(index) => self[index.as_core_type_id().unwrap()]
+                    .composite_type
+                    .is_shared(),
+            },
+        }
+    }
+
     /// Get the top type of the given heap type.
     ///
     /// Concrete types must have had their indices canonicalized to core type
     /// ids, otherwise this method will panic.
     pub fn top_type(&self, heap_type: &HeapType) -> HeapType {
+        use AbstractHeapType::*;
         match *heap_type {
             HeapType::Concrete(idx) => match self[idx.as_core_type_id().unwrap()].composite_type {
-                CompositeType::Func(_) => HeapType::Func,
-                CompositeType::Array(_) | CompositeType::Struct(_) => HeapType::Any,
+                CompositeType::Func(_) => HeapType::Abstract {
+                    shared: false, // TODO: handle shared--retrieve from `func` type.
+                    ty: Func,
+                },
+                CompositeType::Array(_) | CompositeType::Struct(_) => HeapType::Abstract {
+                    shared: false, // TODO: handle shared--retrieve from `array` or `struct` type.
+                    ty: Any,
+                },
             },
-            HeapType::Func | HeapType::NoFunc => HeapType::Func,
-            HeapType::Extern | HeapType::NoExtern => HeapType::Extern,
-            HeapType::Any
-            | HeapType::Eq
-            | HeapType::Struct
-            | HeapType::Array
-            | HeapType::I31
-            | HeapType::None => HeapType::Any,
-            HeapType::Exn | HeapType::NoExn => HeapType::Exn,
+            HeapType::Abstract { shared, ty } => {
+                let ty = match ty {
+                    Func | NoFunc => Func,
+                    Extern | NoExtern => Extern,
+                    Any | Eq | Struct | Array | I31 | None => Any,
+                    Exn | NoExn => Exn,
+                };
+                HeapType::Abstract { shared, ty }
+            }
         }
     }
 
diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs
index bf342e7a..5b86607d 100644
--- a/crates/wasmprinter/src/lib.rs
+++ b/crates/wasmprinter/src/lib.rs
@@ -1018,21 +1018,32 @@ impl Printer<'_, '_> {
 
     fn print_heaptype(&mut self, state: &State, ty: HeapType) -> Result<()> {
         match ty {
-            HeapType::Func => self.print_type_keyword("func")?,
-            HeapType::Extern => self.print_type_keyword("extern")?,
-            HeapType::Any => self.print_type_keyword("any")?,
-            HeapType::None => self.print_type_keyword("none")?,
-            HeapType::NoExtern => self.print_type_keyword("noextern")?,
-            HeapType::NoFunc => self.print_type_keyword("nofunc")?,
-            HeapType::Eq => self.print_type_keyword("eq")?,
-            HeapType::Struct => self.print_type_keyword("struct")?,
-            HeapType::Array => self.print_type_keyword("array")?,
-            HeapType::I31 => self.print_type_keyword("i31")?,
-            HeapType::Exn => self.print_type_keyword("exn")?,
-            HeapType::NoExn => self.print_type_keyword("noexn")?,
             HeapType::Concrete(i) => {
                 self.print_idx(&state.core.type_names, i.as_module_index().unwrap())?;
             }
+            HeapType::Abstract { shared, ty } => {
+                use AbstractHeapType::*;
+                if shared {
+                    self.start_group("shared ")?;
+                }
+                match ty {
+                    Func => self.print_type_keyword("func")?,
+                    Extern => self.print_type_keyword("extern")?,
+                    Any => self.print_type_keyword("any")?,
+                    None => self.print_type_keyword("none")?,
+                    NoExtern => self.print_type_keyword("noextern")?,
+                    NoFunc => self.print_type_keyword("nofunc")?,
+                    Eq => self.print_type_keyword("eq")?,
+                    Struct => self.print_type_keyword("struct")?,
+                    Array => self.print_type_keyword("array")?,
+                    I31 => self.print_type_keyword("i31")?,
+                    Exn => self.print_type_keyword("exn")?,
+                    NoExn => self.print_type_keyword("noexn")?,
+                }
+                if shared {
+                    self.end_group()?;
+                }
+            }
         }
         Ok(())
     }
diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs
index 68864b0c..a55626e7 100644
--- a/crates/wast/src/component/binary.rs
+++ b/crates/wast/src/component/binary.rs
@@ -594,24 +594,27 @@ impl From<core::RefType<'_>> for wasm_encoder::RefType {
 
 impl From<core::HeapType<'_>> for wasm_encoder::HeapType {
     fn from(r: core::HeapType<'_>) -> Self {
+        use wasm_encoder::AbstractHeapType::*;
         match r {
-            core::HeapType::Func => Self::Func,
-            core::HeapType::Extern => Self::Extern,
-            core::HeapType::Exn | core::HeapType::NoExn => {
-                todo!("encoding of exceptions proposal types not yet implemented")
-            }
+            core::HeapType::Abstract { shared, ty } => match ty {
+                core::AbstractHeapType::Func => Self::Abstract { shared, ty: Func },
+                core::AbstractHeapType::Extern => Self::Abstract { shared, ty: Extern },
+                core::AbstractHeapType::Exn | core::AbstractHeapType::NoExn => {
+                    todo!("encoding of exceptions proposal types not yet implemented")
+                }
+                core::AbstractHeapType::Any
+                | core::AbstractHeapType::Eq
+                | core::AbstractHeapType::Struct
+                | core::AbstractHeapType::Array
+                | core::AbstractHeapType::NoFunc
+                | core::AbstractHeapType::NoExtern
+                | core::AbstractHeapType::None
+                | core::AbstractHeapType::I31 => {
+                    todo!("encoding of GC proposal types not yet implemented")
+                }
+            },
             core::HeapType::Concrete(Index::Num(i, _)) => Self::Concrete(i),
             core::HeapType::Concrete(_) => panic!("unresolved index"),
-            core::HeapType::Any
-            | core::HeapType::Eq
-            | core::HeapType::Struct
-            | core::HeapType::Array
-            | core::HeapType::NoFunc
-            | core::HeapType::NoExtern
-            | core::HeapType::None
-            | core::HeapType::I31 => {
-                todo!("encoding of GC proposal types not yet implemented")
-            }
         }
     }
 }
diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs
index cd7fb7e3..ac072c5b 100644
--- a/crates/wast/src/component/resolve.rs
+++ b/crates/wast/src/component/resolve.rs
@@ -513,18 +513,7 @@ impl<'a> Resolver<'a> {
                 match &mut r.rep {
                     ValType::I32 | ValType::I64 | ValType::F32 | ValType::F64 | ValType::V128 => {}
                     ValType::Ref(r) => match &mut r.heap {
-                        core::HeapType::Func
-                        | core::HeapType::Extern
-                        | core::HeapType::Exn
-                        | core::HeapType::Any
-                        | core::HeapType::Eq
-                        | core::HeapType::Array
-                        | core::HeapType::I31
-                        | core::HeapType::Struct
-                        | core::HeapType::None
-                        | core::HeapType::NoFunc
-                        | core::HeapType::NoExtern
-                        | core::HeapType::NoExn => {}
+                        core::HeapType::Abstract { .. } => {}
                         core::HeapType::Concrete(id) => {
                             self.resolve_ns(id, Ns::Type)?;
                         }
diff --git a/crates/wast/src/core/binary.rs b/crates/wast/src/core/binary.rs
index 84d128fa..8899c81d 100644
--- a/crates/wast/src/core/binary.rs
+++ b/crates/wast/src/core/binary.rs
@@ -285,18 +285,12 @@ impl<'a> Encode for ValType<'a> {
 impl<'a> Encode for HeapType<'a> {
     fn encode(&self, e: &mut Vec<u8>) {
         match self {
-            HeapType::Func => e.push(0x70),
-            HeapType::Extern => e.push(0x6f),
-            HeapType::Exn => e.push(0x69),
-            HeapType::Any => e.push(0x6e),
-            HeapType::Eq => e.push(0x6d),
-            HeapType::Struct => e.push(0x6b),
-            HeapType::Array => e.push(0x6a),
-            HeapType::I31 => e.push(0x6c),
-            HeapType::NoFunc => e.push(0x73),
-            HeapType::NoExtern => e.push(0x72),
-            HeapType::NoExn => e.push(0x74),
-            HeapType::None => e.push(0x71),
+            HeapType::Abstract { shared, ty } => {
+                if *shared {
+                    e.push(0x65);
+                }
+                ty.encode(e)
+            }
             // Note that this is encoded as a signed leb128 so be sure to cast
             // to an i64 first
             HeapType::Concrete(Index::Num(n, _)) => i64::from(*n).encode(e),
@@ -307,61 +301,42 @@ impl<'a> Encode for HeapType<'a> {
     }
 }
 
+impl<'a> Encode for AbstractHeapType {
+    fn encode(&self, e: &mut Vec<u8>) {
+        use AbstractHeapType::*;
+        match self {
+            Func => e.push(0x70),
+            Extern => e.push(0x6f),
+            Exn => e.push(0x69),
+            Any => e.push(0x6e),
+            Eq => e.push(0x6d),
+            Struct => e.push(0x6b),
+            Array => e.push(0x6a),
+            I31 => e.push(0x6c),
+            NoFunc => e.push(0x73),
+            NoExtern => e.push(0x72),
+            NoExn => e.push(0x74),
+            None => e.push(0x71),
+        }
+    }
+}
+
 impl<'a> Encode for RefType<'a> {
     fn encode(&self, e: &mut Vec<u8>) {
         match self {
-            // The 'funcref' binary abbreviation
-            RefType {
-                nullable: true,
-                heap: HeapType::Func,
-            } => e.push(0x70),
-            // The 'externref' binary abbreviation
-            RefType {
-                nullable: true,
-                heap: HeapType::Extern,
-            } => e.push(0x6f),
-            // The 'exnref' binary abbreviation
-            RefType {
-                nullable: true,
-                heap: HeapType::Exn,
-            } => e.push(0x69),
-            // The 'eqref' binary abbreviation
-            RefType {
-                nullable: true,
-                heap: HeapType::Eq,
-            } => e.push(0x6d),
-            // The 'structref' binary abbreviation
+            // Binary abbreviations (i.e., short form), for when the ref is
+            // nullable.
             RefType {
                 nullable: true,
-                heap: HeapType::Struct,
-            } => e.push(0x6b),
-            // The 'i31ref' binary abbreviation
-            RefType {
-                nullable: true,
-                heap: HeapType::I31,
-            } => e.push(0x6c),
-            // The 'nullfuncref' binary abbreviation
-            RefType {
-                nullable: true,
-                heap: HeapType::NoFunc,
-            } => e.push(0x73),
-            // The 'nullexternref' binary abbreviation
-            RefType {
-                nullable: true,
-                heap: HeapType::NoExtern,
-            } => e.push(0x72),
-            // The 'nullexnref' binary abbreviation
-            RefType {
-                nullable: true,
-                heap: HeapType::NoExn,
-            } => e.push(0x74),
-            // The 'nullref' binary abbreviation
-            RefType {
-                nullable: true,
-                heap: HeapType::None,
-            } => e.push(0x71),
+                heap: HeapType::Abstract { shared, ty },
+            } => {
+                if *shared {
+                    e.push(0x65);
+                }
+                ty.encode(e);
+            }
 
-            // Generic 'ref null <heaptype>' encoding
+            // Generic 'ref null <heaptype>' encoding (i.e., long form).
             RefType {
                 nullable: true,
                 heap,
@@ -369,7 +344,8 @@ impl<'a> Encode for RefType<'a> {
                 e.push(0x63);
                 heap.encode(e);
             }
-            // Generic 'ref <heaptype>' encoding
+
+            // Generic 'ref <heaptype>' encoding.
             RefType {
                 nullable: false,
                 heap,
@@ -606,7 +582,11 @@ impl Encode for Elem<'_> {
                     ty:
                         RefType {
                             nullable: true,
-                            heap: HeapType::Func,
+                            heap:
+                                HeapType::Abstract {
+                                    shared: false,
+                                    ty: AbstractHeapType::Func,
+                                },
                         },
                     ..
                 },
diff --git a/crates/wast/src/core/types.rs b/crates/wast/src/core/types.rs
index 266bf1d1..b149f33d 100644
--- a/crates/wast/src/core/types.rs
+++ b/crates/wast/src/core/types.rs
@@ -1,5 +1,6 @@
 use crate::core::*;
 use crate::kw;
+use crate::parser::Lookahead1;
 use crate::parser::{Cursor, Parse, Parser, Peek, Result};
 use crate::token::{Id, Index, LParen, NameAnnotation, Span};
 use crate::Error;
@@ -57,10 +58,56 @@ impl<'a> Peek for ValType<'a> {
     }
 }
 
-/// A heap type for a reference type
+/// A heap type for a reference type.
 #[allow(missing_docs)]
 #[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
 pub enum HeapType<'a> {
+    /// An abstract reference. With the shared-everything-threads proposal,
+    /// these types can also be marked `shared`.
+    Abstract { shared: bool, ty: AbstractHeapType },
+    /// A reference to a concrete function, struct, or array type defined by
+    /// Wasm: `ref T`. This is part of the function references and GC proposals.
+    Concrete(Index<'a>),
+}
+
+impl<'a> Parse<'a> for HeapType<'a> {
+    fn parse(parser: Parser<'a>) -> Result<Self> {
+        let mut l = parser.lookahead1();
+        if l.peek::<Index>()? {
+            Ok(HeapType::Concrete(parser.parse()?))
+        } else if l.peek::<LParen>()? {
+            parser.parens(|p| {
+                p.parse::<kw::shared>()?;
+                Ok(HeapType::Abstract {
+                    shared: true,
+                    ty: p.parse()?,
+                })
+            })
+        } else if l.peek::<AbstractHeapType>()? {
+            Ok(HeapType::Abstract {
+                shared: false,
+                ty: parser.parse()?,
+            })
+        } else {
+            Err(l.error())
+        }
+    }
+}
+
+impl<'a> Peek for HeapType<'a> {
+    fn peek(cursor: Cursor<'_>) -> Result<bool> {
+        Ok(AbstractHeapType::peek(cursor)?
+            || (LParen::peek(cursor)? && kw::shared::peek2(cursor)?)
+            || (LParen::peek(cursor)? && kw::r#type::peek2(cursor)?))
+    }
+    fn display() -> &'static str {
+        "heaptype"
+    }
+}
+
+/// An abstract heap type.
+#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
+pub enum AbstractHeapType {
     /// An untyped function reference: funcref. This is part of the reference
     /// types proposal.
     Func,
@@ -89,59 +136,54 @@ pub enum HeapType<'a> {
     None,
     /// The bottom type of the exnref hierarchy. Part of the exceptions proposal.
     NoExn,
-    /// A reference to a concrete function, struct, or array type defined by
-    /// Wasm: `ref T`. This is part of the function references and GC proposals.
-    Concrete(Index<'a>),
 }
 
-impl<'a> Parse<'a> for HeapType<'a> {
+impl<'a> Parse<'a> for AbstractHeapType {
     fn parse(parser: Parser<'a>) -> Result<Self> {
         let mut l = parser.lookahead1();
         if l.peek::<kw::func>()? {
             parser.parse::<kw::func>()?;
-            Ok(HeapType::Func)
+            Ok(AbstractHeapType::Func)
         } else if l.peek::<kw::r#extern>()? {
             parser.parse::<kw::r#extern>()?;
-            Ok(HeapType::Extern)
+            Ok(AbstractHeapType::Extern)
         } else if l.peek::<kw::exn>()? {
             parser.parse::<kw::exn>()?;
-            Ok(HeapType::Exn)
+            Ok(AbstractHeapType::Exn)
         } else if l.peek::<kw::r#any>()? {
             parser.parse::<kw::r#any>()?;
-            Ok(HeapType::Any)
+            Ok(AbstractHeapType::Any)
         } else if l.peek::<kw::eq>()? {
             parser.parse::<kw::eq>()?;
-            Ok(HeapType::Eq)
+            Ok(AbstractHeapType::Eq)
         } else if l.peek::<kw::r#struct>()? {
             parser.parse::<kw::r#struct>()?;
-            Ok(HeapType::Struct)
+            Ok(AbstractHeapType::Struct)
         } else if l.peek::<kw::array>()? {
             parser.parse::<kw::array>()?;
-            Ok(HeapType::Array)
+            Ok(AbstractHeapType::Array)
         } else if l.peek::<kw::i31>()? {
             parser.parse::<kw::i31>()?;
-            Ok(HeapType::I31)
+            Ok(AbstractHeapType::I31)
         } else if l.peek::<kw::nofunc>()? {
             parser.parse::<kw::nofunc>()?;
-            Ok(HeapType::NoFunc)
+            Ok(AbstractHeapType::NoFunc)
         } else if l.peek::<kw::noextern>()? {
             parser.parse::<kw::noextern>()?;
-            Ok(HeapType::NoExtern)
+            Ok(AbstractHeapType::NoExtern)
         } else if l.peek::<kw::noexn>()? {
             parser.parse::<kw::noexn>()?;
-            Ok(HeapType::NoExn)
+            Ok(AbstractHeapType::NoExn)
         } else if l.peek::<kw::none>()? {
             parser.parse::<kw::none>()?;
-            Ok(HeapType::None)
-        } else if l.peek::<Index>()? {
-            Ok(HeapType::Concrete(parser.parse()?))
+            Ok(AbstractHeapType::None)
         } else {
             Err(l.error())
         }
     }
 }
 
-impl<'a> Peek for HeapType<'a> {
+impl<'a> Peek for AbstractHeapType {
     fn peek(cursor: Cursor<'_>) -> Result<bool> {
         Ok(kw::func::peek(cursor)?
             || kw::r#extern::peek(cursor)?
@@ -154,11 +196,10 @@ impl<'a> Peek for HeapType<'a> {
             || kw::nofunc::peek(cursor)?
             || kw::noextern::peek(cursor)?
             || kw::noexn::peek(cursor)?
-            || kw::none::peek(cursor)?
-            || (LParen::peek(cursor)? && kw::r#type::peek2(cursor)?))
+            || kw::none::peek(cursor)?)
     }
     fn display() -> &'static str {
-        "heaptype"
+        "absheaptype"
     }
 }
 
@@ -175,7 +216,10 @@ impl<'a> RefType<'a> {
     pub fn func() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::Func,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::Func,
+            },
         }
     }
 
@@ -183,7 +227,10 @@ impl<'a> RefType<'a> {
     pub fn r#extern() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::Extern,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::Extern,
+            },
         }
     }
 
@@ -191,7 +238,10 @@ impl<'a> RefType<'a> {
     pub fn exn() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::Exn,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::Exn,
+            },
         }
     }
 
@@ -199,7 +249,10 @@ impl<'a> RefType<'a> {
     pub fn any() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::Any,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::Any,
+            },
         }
     }
 
@@ -207,7 +260,10 @@ impl<'a> RefType<'a> {
     pub fn eq() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::Eq,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::Eq,
+            },
         }
     }
 
@@ -215,7 +271,10 @@ impl<'a> RefType<'a> {
     pub fn r#struct() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::Struct,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::Struct,
+            },
         }
     }
 
@@ -223,7 +282,10 @@ impl<'a> RefType<'a> {
     pub fn array() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::Array,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::Array,
+            },
         }
     }
 
@@ -231,7 +293,10 @@ impl<'a> RefType<'a> {
     pub fn i31() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::I31,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::I31,
+            },
         }
     }
 
@@ -239,7 +304,10 @@ impl<'a> RefType<'a> {
     pub fn nullfuncref() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::NoFunc,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::NoFunc,
+            },
         }
     }
 
@@ -247,7 +315,10 @@ impl<'a> RefType<'a> {
     pub fn nullexternref() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::NoExtern,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::NoExtern,
+            },
         }
     }
 
@@ -255,7 +326,10 @@ impl<'a> RefType<'a> {
     pub fn nullref() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::None,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::None,
+            },
         }
     }
 
@@ -263,14 +337,46 @@ impl<'a> RefType<'a> {
     pub fn nullexnref() -> Self {
         RefType {
             nullable: true,
-            heap: HeapType::NoExn,
+            heap: HeapType::Abstract {
+                shared: false,
+                ty: AbstractHeapType::NoExn,
+            },
         }
     }
-}
 
-impl<'a> Parse<'a> for RefType<'a> {
-    fn parse(parser: Parser<'a>) -> Result<Self> {
-        let mut l = parser.lookahead1();
+    /// Make the reference type a `shared` one.
+    ///
+    /// Note that this is not possible for concrete references (e.g., `(ref
+    /// $t)`) so `None` is returned in that case.
+    pub fn shared(self) -> Option<Self> {
+        match self.heap {
+            HeapType::Abstract { ty, .. } => Some(RefType {
+                nullable: self.nullable,
+                heap: HeapType::Abstract { shared: true, ty },
+            }),
+            _ => None,
+        }
+    }
+
+    /// Helper for checking if shorthand forms of reference types can be parsed
+    /// next; e.g., `funcref`.
+    fn peek_shorthand(l: &mut Lookahead1) -> Result<bool> {
+        Ok(l.peek::<kw::funcref>()?
+            || l.peek::<kw::externref>()?
+            || l.peek::<kw::exnref>()?
+            || l.peek::<kw::anyref>()?
+            || l.peek::<kw::eqref>()?
+            || l.peek::<kw::structref>()?
+            || l.peek::<kw::arrayref>()?
+            || l.peek::<kw::i31ref>()?
+            || l.peek::<kw::nullfuncref>()?
+            || l.peek::<kw::nullexternref>()?
+            || l.peek::<kw::nullexnref>()?
+            || l.peek::<kw::nullref>()?)
+    }
+
+    /// Helper for parsing shorthand forms of reference types; e.g., `funcref`.
+    fn parse_shorthand(mut l: Lookahead1, parser: Parser<'a>) -> Result<Self> {
         if l.peek::<kw::funcref>()? {
             parser.parse::<kw::funcref>()?;
             Ok(RefType::func())
@@ -307,10 +413,23 @@ impl<'a> Parse<'a> for RefType<'a> {
         } else if l.peek::<kw::nullref>()? {
             parser.parse::<kw::nullref>()?;
             Ok(RefType::nullref())
+        } else {
+            Err(l.error())
+        }
+    }
+}
+
+impl<'a> Parse<'a> for RefType<'a> {
+    fn parse(parser: Parser<'a>) -> Result<Self> {
+        let mut l = parser.lookahead1();
+        if RefType::peek_shorthand(&mut l)? {
+            // I.e., `*ref`.
+            RefType::parse_shorthand(l, parser)
         } else if l.peek::<LParen>()? {
             parser.parens(|p| {
                 let mut l = parser.lookahead1();
                 if l.peek::<kw::r#ref>()? {
+                    // I.e., `(ref null? ...)`.
                     p.parse::<kw::r#ref>()?;
 
                     let mut nullable = false;
@@ -323,6 +442,11 @@ impl<'a> Parse<'a> for RefType<'a> {
                         nullable,
                         heap: parser.parse()?,
                     })
+                } else if l.peek::<kw::shared>()? {
+                    // I.e., `(shared *ref)`.
+                    p.parse::<kw::shared>()?;
+                    let reftype = RefType::parse_shorthand(l, parser)?;
+                    Ok(reftype.shared().expect("only abstract heap types are used"))
                 } else {
                     Err(l.error())
                 }
@@ -347,6 +471,7 @@ impl<'a> Peek for RefType<'a> {
             || kw::nullexternref::peek(cursor)?
             || kw::nullexnref::peek(cursor)?
             || kw::nullref::peek(cursor)?
+            || (LParen::peek(cursor)? && kw::shared::peek2(cursor)?)
             || (LParen::peek(cursor)? && kw::r#ref::peek2(cursor)?))
     }
     fn display() -> &'static str {
diff --git a/crates/wit-component/Cargo.toml b/crates/wit-component/Cargo.toml
index 8bd969c4..c05f13e6 100644
--- a/crates/wit-component/Cargo.toml
+++ b/crates/wit-component/Cargo.toml
@@ -18,7 +18,7 @@ workspace = true
 
 [dependencies]
 wasmparser = { workspace = true }
-wasm-encoder = { workspace = true }
+wasm-encoder = { workspace = true, features = ["wasmparser"] }
 wasm-metadata = { workspace = true }
 wit-parser = { workspace = true, features = ['decoding', 'serde'] }
 anyhow = { workspace = true }
diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs
index 6f0a5b61..bb857f94 100644
--- a/crates/wit-component/src/gc.rs
+++ b/crates/wit-component/src/gc.rs
@@ -486,18 +486,7 @@ impl<'a> Module<'a> {
 
     fn heapty(&mut self, ty: HeapType) {
         match ty {
-            HeapType::Func
-            | HeapType::Extern
-            | HeapType::Any
-            | HeapType::None
-            | HeapType::NoExtern
-            | HeapType::NoFunc
-            | HeapType::Eq
-            | HeapType::Struct
-            | HeapType::Array
-            | HeapType::I31
-            | HeapType::Exn
-            | HeapType::NoExn => {}
+            HeapType::Abstract { .. } => {}
             HeapType::Concrete(i) => self.ty(i.as_module_index().unwrap()),
         }
     }
@@ -1132,21 +1121,13 @@ impl Encoder {
 
     fn heapty(&self, ht: wasmparser::HeapType) -> wasm_encoder::HeapType {
         match ht {
-            HeapType::Func => wasm_encoder::HeapType::Func,
-            HeapType::Extern => wasm_encoder::HeapType::Extern,
-            HeapType::Any => wasm_encoder::HeapType::Any,
-            HeapType::None => wasm_encoder::HeapType::None,
-            HeapType::NoExtern => wasm_encoder::HeapType::NoExtern,
-            HeapType::NoFunc => wasm_encoder::HeapType::NoFunc,
-            HeapType::Eq => wasm_encoder::HeapType::Eq,
-            HeapType::Struct => wasm_encoder::HeapType::Struct,
-            HeapType::Array => wasm_encoder::HeapType::Array,
-            HeapType::I31 => wasm_encoder::HeapType::I31,
-            HeapType::Exn => wasm_encoder::HeapType::Exn,
-            HeapType::NoExn => wasm_encoder::HeapType::NoExn,
             HeapType::Concrete(idx) => {
                 wasm_encoder::HeapType::Concrete(self.types.remap(idx.as_module_index().unwrap()))
             }
+            HeapType::Abstract { shared, ty } => {
+                let ty = ty.into();
+                wasm_encoder::HeapType::Abstract { shared, ty }
+            }
         }
     }
 }
diff --git a/crates/wit-component/src/linking.rs b/crates/wit-component/src/linking.rs
index d47aca56..e3923c1e 100644
--- a/crates/wit-component/src/linking.rs
+++ b/crates/wit-component/src/linking.rs
@@ -35,9 +35,9 @@ use {
     },
     wasm_encoder::{
         CodeSection, ConstExpr, DataSection, ElementSection, Elements, EntityType, ExportKind,
-        ExportSection, Function, FunctionSection, GlobalSection, HeapType, ImportSection,
-        Instruction as Ins, MemArg, MemorySection, MemoryType, Module, RawCustomSection, RefType,
-        StartSection, TableSection, TableType, TypeSection, ValType,
+        ExportSection, Function, FunctionSection, GlobalSection, ImportSection, Instruction as Ins,
+        MemArg, MemorySection, MemoryType, Module, RawCustomSection, RefType, StartSection,
+        TableSection, TableType, TypeSection, ValType,
     },
     wasmparser::SymbolFlags,
 };
@@ -464,10 +464,7 @@ fn make_env_module<'a>(
     {
         let mut tables = TableSection::new();
         tables.table(TableType {
-            element_type: RefType {
-                nullable: true,
-                heap_type: HeapType::Func,
-            },
+            element_type: RefType::FUNCREF,
             minimum: table_offset.into(),
             maximum: None,
             table64: false,
@@ -559,10 +556,7 @@ fn make_init_module(
         "env",
         "__indirect_function_table",
         TableType {
-            element_type: RefType {
-                nullable: true,
-                heap_type: HeapType::Func,
-            },
+            element_type: RefType::FUNCREF,
             minimum: 0,
             maximum: None,
             table64: false,
diff --git a/src/bin/wasm-tools/json_from_wast.rs b/src/bin/wasm-tools/json_from_wast.rs
index c85cb946..afb99243 100644
--- a/src/bin/wasm-tools/json_from_wast.rs
+++ b/src/bin/wasm-tools/json_from_wast.rs
@@ -1,7 +1,7 @@
 use anyhow::{bail, Context, Result};
 use clap::Parser;
 use std::path::{Path, PathBuf};
-use wast::core::{HeapType, NanPattern, V128Const, V128Pattern, WastRetCore};
+use wast::core::{AbstractHeapType, HeapType, NanPattern, V128Const, V128Pattern, WastRetCore};
 use wast::lexer::Lexer;
 use wast::parser::{self, ParseBuffer};
 use wast::token::{Span, F32, F64};
@@ -506,21 +506,30 @@ fn f64_to_string(f: F64) -> String {
 
 fn null_heap_ty(ty: HeapType<'_>) -> Result<json::Const> {
     Ok(match ty {
-        HeapType::Func => json::Const::FuncRef {
-            value: Some("null".to_string()),
-        },
-        HeapType::Extern => json::Const::ExternRef {
-            value: "null".to_string(),
-        },
-        HeapType::Any => json::Const::AnyRef {
-            value: Some("null".to_string()),
-        },
-        HeapType::None => json::Const::NullRef,
-        HeapType::NoFunc => json::Const::NullFuncRef,
-        HeapType::NoExtern => json::Const::NullExternRef,
-        HeapType::Exn => json::Const::ExnRef {
-            value: Some("null".to_string()),
-        },
+        HeapType::Abstract { shared, ty } => {
+            use AbstractHeapType::*;
+            if shared {
+                bail!("shared abstract types are not supported in `ref.null`")
+            }
+            match ty {
+                Func => json::Const::FuncRef {
+                    value: Some("null".to_string()),
+                },
+                Extern => json::Const::ExternRef {
+                    value: "null".to_string(),
+                },
+                Any => json::Const::AnyRef {
+                    value: Some("null".to_string()),
+                },
+                None => json::Const::NullRef,
+                NoFunc => json::Const::NullFuncRef,
+                NoExtern => json::Const::NullExternRef,
+                Exn => json::Const::ExnRef {
+                    value: Some("null".to_string()),
+                },
+                _ => bail!("unsupported abstract type found in `ref.null`"),
+            }
+        }
         _ => bail!("unsupported heap type found in `ref.null`"),
     })
 }


Test patch:
diff --git a/tests/local/shared-everything-threads/global-heap-types.wast b/tests/local/shared-everything-threads/global-heap-types.wast
new file mode 100644
index 00000000..4b887fc1
--- /dev/null
+++ b/tests/local/shared-everything-threads/global-heap-types.wast
@@ -0,0 +1,311 @@
+;; Check shared attributes for global heap types.
+
+;; Concrete heap types cannot be marked shared yet (TODO: this is only possible
+;; once composite types can be marked shared).
+;;
+;; (assert_invalid
+;;   (module
+;;     (type $t (shared anyref))
+;;     (global (shared (ref null (shared $t))))))
+;;   "shared value type")
+
+;; `func` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_funcref") (shared (shared funcref)))
+  (global (import "spectest" "global_mut_funcref") (shared mut (shared funcref)))
+  (global (import "spectest" "global_ref_null_func") (shared (ref null (shared func))))
+  (global (import "spectest" "global_mut_ref_null_func") (shared mut (ref null (shared func))))
+  (global (import "spectest" "global_ref_func") (shared (ref (shared func))))
+  (global (import "spectest" "global_mut_ref_func") (shared mut (ref (shared func))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared funcref)) (ref.null (shared func)))
+  (global (shared (ref null (shared func))) (ref.null (shared func)))
+)
+
+(assert_invalid
+  (module (global (shared funcref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null func))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared func))))
+  "type mismatch")
+
+;; `extern` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_externref") (shared (shared externref)))
+  (global (import "spectest" "global_mut_externref") (shared mut (shared externref)))
+  (global (import "spectest" "global_ref_null_extern") (shared (ref null (shared extern))))
+  (global (import "spectest" "global_mut_ref_null_extern") (shared mut (ref null (shared extern))))
+  (global (import "spectest" "global_ref_extern") (shared (ref (shared extern))))
+  (global (import "spectest" "global_mut_ref_extern") (shared mut (ref (shared extern))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared externref)) (ref.null (shared extern)))
+  (global (shared (ref null (shared extern))) (ref.null (shared extern)))
+)
+
+(assert_invalid
+  (module (global (shared externref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null extern))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared extern))))
+  "type mismatch")
+
+;; `exn` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_exnref") (shared (shared exnref)))
+  (global (import "spectest" "global_mut_exnref") (shared mut (shared exnref)))
+  (global (import "spectest" "global_ref_null_exn") (shared (ref null (shared exn))))
+  (global (import "spectest" "global_mut_ref_null_exn") (shared mut (ref null (shared exn))))
+  (global (import "spectest" "global_ref_exn") (shared (ref (shared exn))))
+  (global (import "spectest" "global_mut_ref_exn") (shared mut (ref (shared exn))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared exnref)) (ref.null (shared exn)))
+  (global (shared (ref null (shared exn))) (ref.null (shared exn)))
+)
+
+(assert_invalid
+  (module (global (shared exnref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null exn))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared exn))))
+  "type mismatch")
+
+;; `any` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_anyref") (shared (shared anyref)))
+  (global (import "spectest" "global_mut_anyref") (shared mut (shared anyref)))
+  (global (import "spectest" "global_ref_null_any") (shared (ref null (shared any))))
+  (global (import "spectest" "global_mut_ref_null_any") (shared mut (ref null (shared any))))
+  (global (import "spectest" "global_ref_any") (shared (ref (shared any))))
+  (global (import "spectest" "global_mut_ref_any") (shared mut (ref (shared any))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared anyref)) (ref.null (shared any)))
+  (global (shared (ref null (shared any))) (ref.null (shared any)))
+)
+
+(assert_invalid
+  (module (global (shared anyref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null any))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared any))))
+  "type mismatch")
+
+;; `eq` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_eqref") (shared (shared eqref)))
+  (global (import "spectest" "global_mut_eqref") (shared mut (shared eqref)))
+  (global (import "spectest" "global_ref_null_eq") (shared (ref null (shared eq))))
+  (global (import "spectest" "global_mut_ref_null_eq") (shared mut (ref null (shared eq))))
+  (global (import "spectest" "global_ref_eq") (shared (ref (shared eq))))
+  (global (import "spectest" "global_mut_ref_eq") (shared mut (ref (shared eq))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared eqref)) (ref.null (shared eq)))
+  (global (shared (ref null (shared eq))) (ref.null (shared eq)))
+)
+
+(assert_invalid
+  (module (global (shared eqref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null eq))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared eq))))
+  "type mismatch")
+
+;; `struct` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_structref") (shared (shared structref)))
+  (global (import "spectest" "global_mut_structref") (shared mut (shared structref)))
+  (global (import "spectest" "global_ref_null_struct") (shared (ref null (shared struct))))
+  (global (import "spectest" "global_mut_ref_null_struct") (shared mut (ref null (shared struct))))
+  (global (import "spectest" "global_ref_struct") (shared (ref (shared struct))))
+  (global (import "spectest" "global_mut_ref_struct") (shared mut (ref (shared struct))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared structref)) (ref.null (shared struct)))
+  (global (shared (ref null (shared struct))) (ref.null (shared struct)))
+)
+
+(assert_invalid
+  (module (global (shared structref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null struct))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared struct))))
+  "type mismatch")
+
+;; `array` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_arrayref") (shared (shared arrayref)))
+  (global (import "spectest" "global_mut_arrayref") (shared mut (shared arrayref)))
+  (global (import "spectest" "global_ref_null_array") (shared (ref null (shared array))))
+  (global (import "spectest" "global_mut_ref_null_array") (shared mut (ref null (shared array))))
+  (global (import "spectest" "global_ref_array") (shared (ref (shared array))))
+  (global (import "spectest" "global_mut_ref_array") (shared mut (ref (shared array))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared arrayref)) (ref.null (shared array)))
+  (global (shared (ref null (shared array))) (ref.null (shared array)))
+)
+
+(assert_invalid
+  (module (global (shared arrayref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null array))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared array))))
+  "type mismatch")
+
+;; `i31` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_i31ref") (shared (shared i31ref)))
+  (global (import "spectest" "global_mut_i31ref") (shared mut (shared i31ref)))
+  (global (import "spectest" "global_ref_null_i31") (shared (ref null (shared i31))))
+  (global (import "spectest" "global_mut_ref_null_i31") (shared mut (ref null (shared i31))))
+  (global (import "spectest" "global_ref_i31") (shared (ref (shared i31))))
+  (global (import "spectest" "global_mut_ref_i31") (shared mut (ref (shared i31))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared i31ref)) (ref.null (shared i31)))
+  (global (shared (ref null (shared i31))) (ref.null (shared i31)))
+)
+
+(assert_invalid
+  (module (global (shared i31ref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null i31))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared i31))))
+  "type mismatch")
+
+;; `nofunc` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_nullfuncref") (shared (shared nullfuncref)))
+  (global (import "spectest" "global_mut_nullfuncref") (shared mut (shared nullfuncref)))
+  (global (import "spectest" "global_ref_null_nofunc") (shared (ref null (shared nofunc))))
+  (global (import "spectest" "global_mut_ref_null_nofunc") (shared mut (ref null (shared nofunc))))
+  (global (import "spectest" "global_ref_nofunc") (shared (ref (shared nofunc))))
+  (global (import "spectest" "global_mut_ref_nofunc") (shared mut (ref (shared nofunc))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared nullfuncref)) (ref.null (shared nofunc)))
+  (global (shared (ref null (shared nofunc))) (ref.null (shared nofunc)))
+)
+
+(assert_invalid
+  (module (global (shared nullfuncref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null nofunc))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared nofunc))))
+  "type mismatch")
+
+;; `noextern` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_nullexternref") (shared (shared nullexternref)))
+  (global (import "spectest" "global_mut_nullexternref") (shared mut (shared nullexternref)))
+  (global (import "spectest" "global_ref_null_noextern") (shared (ref null (shared noextern))))
+  (global (import "spectest" "global_mut_ref_null_noextern") (shared mut (ref null (shared noextern))))
+  (global (import "spectest" "global_ref_noextern") (shared (ref (shared noextern))))
+  (global (import "spectest" "global_mut_ref_noextern") (shared mut (ref (shared noextern))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared nullexternref)) (ref.null (shared noextern)))
+  (global (shared (ref null (shared noextern))) (ref.null (shared noextern)))
+)
+
+(assert_invalid
+  (module (global (shared nullexternref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null noextern))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared noextern))))
+  "type mismatch")
+
+;; `noexn` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_nullexnref") (shared (shared nullexnref)))
+  (global (import "spectest" "global_mut_nullexnref") (shared mut (shared nullexnref)))
+  (global (import "spectest" "global_ref_null_noexn") (shared (ref null (shared noexn))))
+  (global (import "spectest" "global_mut_ref_null_noexn") (shared mut (ref null (shared noexn))))
+  (global (import "spectest" "global_ref_noexn") (shared (ref (shared noexn))))
+  (global (import "spectest" "global_mut_ref_noexn") (shared mut (ref (shared noexn))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared nullexnref)) (ref.null (shared noexn)))
+  (global (shared (ref null (shared noexn))) (ref.null (shared noexn)))
+)
+
+(assert_invalid
+  (module (global (shared nullexnref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null noexn))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared noexn))))
+  "type mismatch")
+
+;; `none` references.
+(module
+  ;; Imported (long/short forms, mut, null).
+  (global (import "spectest" "global_nullref") (shared (shared nullref)))
+  (global (import "spectest" "global_mut_nullref") (shared mut (shared nullref)))
+  (global (import "spectest" "global_ref_null_none") (shared (ref null (shared none))))
+  (global (import "spectest" "global_mut_ref_null_none") (shared mut (ref null (shared none))))
+  (global (import "spectest" "global_ref_none") (shared (ref (shared none))))
+  (global (import "spectest" "global_mut_ref_none") (shared mut (ref (shared none))))
+
+  ;; Initialized (long/short form).
+  (global (shared (shared nullref)) (ref.null (shared none)))
+  (global (shared (ref null (shared none))) (ref.null (shared none)))
+)
+
+(assert_invalid
+  (module (global (shared nullref)))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (shared (ref null none))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module (global (ref (shared none))))
+  "type mismatch")
+
diff --git a/tests/local/shared-everything-threads/global.wast b/tests/local/shared-everything-threads/global.wast
index ad33bf2b..9d47be9f 100644
--- a/tests/local/shared-everything-threads/global.wast
+++ b/tests/local/shared-everything-threads/global.wast
@@ -35,7 +35,6 @@
   "shared value type")
 
 ;; Check global.atomic.get, global.atomic.set.
-
 (module
   (global $a (import "spectest" "global_i32") (shared i32))
   (global $b (import "spectest" "global_i64") (shared mut i64))
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast.json b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast.json
new file mode 100644
index 00000000..526b9da7
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast.json
@@ -0,0 +1,317 @@
+{
+  "source_filename": "tests/local/shared-everything-threads/global-heap-types.wast",
+  "commands": [
+    {
+      "type": "module",
+      "line": 13,
+      "filename": "global-heap-types.0.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 28,
+      "filename": "global-heap-types.1.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 31,
+      "filename": "global-heap-types.2.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 34,
+      "filename": "global-heap-types.3.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 38,
+      "filename": "global-heap-types.4.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 53,
+      "filename": "global-heap-types.5.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 56,
+      "filename": "global-heap-types.6.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 59,
+      "filename": "global-heap-types.7.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 63,
+      "filename": "global-heap-types.8.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 78,
+      "filename": "global-heap-types.9.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 81,
+      "filename": "global-heap-types.10.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 84,
+      "filename": "global-heap-types.11.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 88,
+      "filename": "global-heap-types.12.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 103,
+      "filename": "global-heap-types.13.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 106,
+      "filename": "global-heap-types.14.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 109,
+      "filename": "global-heap-types.15.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 113,
+      "filename": "global-heap-types.16.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 128,
+      "filename": "global-heap-types.17.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 131,
+      "filename": "global-heap-types.18.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 134,
+      "filename": "global-heap-types.19.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 138,
+      "filename": "global-heap-types.20.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 153,
+      "filename": "global-heap-types.21.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 156,
+      "filename": "global-heap-types.22.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 159,
+      "filename": "global-heap-types.23.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 163,
+      "filename": "global-heap-types.24.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 178,
+      "filename": "global-heap-types.25.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 181,
+      "filename": "global-heap-types.26.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 184,
+      "filename": "global-heap-types.27.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 188,
+      "filename": "global-heap-types.28.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 203,
+      "filename": "global-heap-types.29.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 206,
+      "filename": "global-heap-types.30.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 209,
+      "filename": "global-heap-types.31.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 213,
+      "filename": "global-heap-types.32.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 228,
+      "filename": "global-heap-types.33.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 231,
+      "filename": "global-heap-types.34.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 234,
+      "filename": "global-heap-types.35.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 238,
+      "filename": "global-heap-types.36.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 253,
+      "filename": "global-heap-types.37.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 256,
+      "filename": "global-heap-types.38.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 259,
+      "filename": "global-heap-types.39.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 263,
+      "filename": "global-heap-types.40.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 278,
+      "filename": "global-heap-types.41.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 281,
+      "filename": "global-heap-types.42.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 284,
+      "filename": "global-heap-types.43.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 288,
+      "filename": "global-heap-types.44.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 303,
+      "filename": "global-heap-types.45.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 306,
+      "filename": "global-heap-types.46.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 309,
+      "filename": "global-heap-types.47.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/0.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/0.print
new file mode 100644
index 00000000..c9606e27
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/0.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_funcref" (global (;0;) (shared (ref null (shared func)))))
+  (import "spectest" "global_mut_funcref" (global (;1;) (shared mut (ref null (shared func)))))
+  (import "spectest" "global_ref_null_func" (global (;2;) (shared (ref null (shared func)))))
+  (import "spectest" "global_mut_ref_null_func" (global (;3;) (shared mut (ref null (shared func)))))
+  (import "spectest" "global_ref_func" (global (;4;) (shared (ref (shared func)))))
+  (import "spectest" "global_mut_ref_func" (global (;5;) (shared mut (ref (shared func)))))
+  (global (;6;) (shared (ref null (shared func))) ref.null (shared func))
+  (global (;7;) (shared (ref null (shared func))) ref.null (shared func))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/12.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/12.print
new file mode 100644
index 00000000..0c408cd8
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/12.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_anyref" (global (;0;) (shared (ref null (shared any)))))
+  (import "spectest" "global_mut_anyref" (global (;1;) (shared mut (ref null (shared any)))))
+  (import "spectest" "global_ref_null_any" (global (;2;) (shared (ref null (shared any)))))
+  (import "spectest" "global_mut_ref_null_any" (global (;3;) (shared mut (ref null (shared any)))))
+  (import "spectest" "global_ref_any" (global (;4;) (shared (ref (shared any)))))
+  (import "spectest" "global_mut_ref_any" (global (;5;) (shared mut (ref (shared any)))))
+  (global (;6;) (shared (ref null (shared any))) ref.null (shared any))
+  (global (;7;) (shared (ref null (shared any))) ref.null (shared any))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/16.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/16.print
new file mode 100644
index 00000000..63238fb9
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/16.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_eqref" (global (;0;) (shared (ref null (shared eq)))))
+  (import "spectest" "global_mut_eqref" (global (;1;) (shared mut (ref null (shared eq)))))
+  (import "spectest" "global_ref_null_eq" (global (;2;) (shared (ref null (shared eq)))))
+  (import "spectest" "global_mut_ref_null_eq" (global (;3;) (shared mut (ref null (shared eq)))))
+  (import "spectest" "global_ref_eq" (global (;4;) (shared (ref (shared eq)))))
+  (import "spectest" "global_mut_ref_eq" (global (;5;) (shared mut (ref (shared eq)))))
+  (global (;6;) (shared (ref null (shared eq))) ref.null (shared eq))
+  (global (;7;) (shared (ref null (shared eq))) ref.null (shared eq))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/20.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/20.print
new file mode 100644
index 00000000..364673e0
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/20.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_structref" (global (;0;) (shared (ref null (shared struct)))))
+  (import "spectest" "global_mut_structref" (global (;1;) (shared mut (ref null (shared struct)))))
+  (import "spectest" "global_ref_null_struct" (global (;2;) (shared (ref null (shared struct)))))
+  (import "spectest" "global_mut_ref_null_struct" (global (;3;) (shared mut (ref null (shared struct)))))
+  (import "spectest" "global_ref_struct" (global (;4;) (shared (ref (shared struct)))))
+  (import "spectest" "global_mut_ref_struct" (global (;5;) (shared mut (ref (shared struct)))))
+  (global (;6;) (shared (ref null (shared struct))) ref.null (shared struct))
+  (global (;7;) (shared (ref null (shared struct))) ref.null (shared struct))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/24.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/24.print
new file mode 100644
index 00000000..834454e7
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/24.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_arrayref" (global (;0;) (shared (ref null (shared array)))))
+  (import "spectest" "global_mut_arrayref" (global (;1;) (shared mut (ref null (shared array)))))
+  (import "spectest" "global_ref_null_array" (global (;2;) (shared (ref null (shared array)))))
+  (import "spectest" "global_mut_ref_null_array" (global (;3;) (shared mut (ref null (shared array)))))
+  (import "spectest" "global_ref_array" (global (;4;) (shared (ref (shared array)))))
+  (import "spectest" "global_mut_ref_array" (global (;5;) (shared mut (ref (shared array)))))
+  (global (;6;) (shared (ref null (shared array))) ref.null (shared array))
+  (global (;7;) (shared (ref null (shared array))) ref.null (shared array))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/28.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/28.print
new file mode 100644
index 00000000..b2ececf7
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/28.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_i31ref" (global (;0;) (shared (ref null (shared i31)))))
+  (import "spectest" "global_mut_i31ref" (global (;1;) (shared mut (ref null (shared i31)))))
+  (import "spectest" "global_ref_null_i31" (global (;2;) (shared (ref null (shared i31)))))
+  (import "spectest" "global_mut_ref_null_i31" (global (;3;) (shared mut (ref null (shared i31)))))
+  (import "spectest" "global_ref_i31" (global (;4;) (shared (ref (shared i31)))))
+  (import "spectest" "global_mut_ref_i31" (global (;5;) (shared mut (ref (shared i31)))))
+  (global (;6;) (shared (ref null (shared i31))) ref.null (shared i31))
+  (global (;7;) (shared (ref null (shared i31))) ref.null (shared i31))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/32.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/32.print
new file mode 100644
index 00000000..8a79cdb6
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/32.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_nullfuncref" (global (;0;) (shared (ref null (shared nofunc)))))
+  (import "spectest" "global_mut_nullfuncref" (global (;1;) (shared mut (ref null (shared nofunc)))))
+  (import "spectest" "global_ref_null_nofunc" (global (;2;) (shared (ref null (shared nofunc)))))
+  (import "spectest" "global_mut_ref_null_nofunc" (global (;3;) (shared mut (ref null (shared nofunc)))))
+  (import "spectest" "global_ref_nofunc" (global (;4;) (shared (ref (shared nofunc)))))
+  (import "spectest" "global_mut_ref_nofunc" (global (;5;) (shared mut (ref (shared nofunc)))))
+  (global (;6;) (shared (ref null (shared nofunc))) ref.null (shared nofunc))
+  (global (;7;) (shared (ref null (shared nofunc))) ref.null (shared nofunc))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/36.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/36.print
new file mode 100644
index 00000000..a4665230
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/36.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_nullexternref" (global (;0;) (shared (ref null (shared noextern)))))
+  (import "spectest" "global_mut_nullexternref" (global (;1;) (shared mut (ref null (shared noextern)))))
+  (import "spectest" "global_ref_null_noextern" (global (;2;) (shared (ref null (shared noextern)))))
+  (import "spectest" "global_mut_ref_null_noextern" (global (;3;) (shared mut (ref null (shared noextern)))))
+  (import "spectest" "global_ref_noextern" (global (;4;) (shared (ref (shared noextern)))))
+  (import "spectest" "global_mut_ref_noextern" (global (;5;) (shared mut (ref (shared noextern)))))
+  (global (;6;) (shared (ref null (shared noextern))) ref.null (shared noextern))
+  (global (;7;) (shared (ref null (shared noextern))) ref.null (shared noextern))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/4.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/4.print
new file mode 100644
index 00000000..be075a72
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/4.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_externref" (global (;0;) (shared (ref null (shared extern)))))
+  (import "spectest" "global_mut_externref" (global (;1;) (shared mut (ref null (shared extern)))))
+  (import "spectest" "global_ref_null_extern" (global (;2;) (shared (ref null (shared extern)))))
+  (import "spectest" "global_mut_ref_null_extern" (global (;3;) (shared mut (ref null (shared extern)))))
+  (import "spectest" "global_ref_extern" (global (;4;) (shared (ref (shared extern)))))
+  (import "spectest" "global_mut_ref_extern" (global (;5;) (shared mut (ref (shared extern)))))
+  (global (;6;) (shared (ref null (shared extern))) ref.null (shared extern))
+  (global (;7;) (shared (ref null (shared extern))) ref.null (shared extern))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/40.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/40.print
new file mode 100644
index 00000000..c1270ef9
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/40.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_nullexnref" (global (;0;) (shared (ref null (shared noexn)))))
+  (import "spectest" "global_mut_nullexnref" (global (;1;) (shared mut (ref null (shared noexn)))))
+  (import "spectest" "global_ref_null_noexn" (global (;2;) (shared (ref null (shared noexn)))))
+  (import "spectest" "global_mut_ref_null_noexn" (global (;3;) (shared mut (ref null (shared noexn)))))
+  (import "spectest" "global_ref_noexn" (global (;4;) (shared (ref (shared noexn)))))
+  (import "spectest" "global_mut_ref_noexn" (global (;5;) (shared mut (ref (shared noexn)))))
+  (global (;6;) (shared (ref null (shared noexn))) ref.null (shared noexn))
+  (global (;7;) (shared (ref null (shared noexn))) ref.null (shared noexn))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/44.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/44.print
new file mode 100644
index 00000000..b141dfd3
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/44.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_nullref" (global (;0;) (shared (ref null (shared none)))))
+  (import "spectest" "global_mut_nullref" (global (;1;) (shared mut (ref null (shared none)))))
+  (import "spectest" "global_ref_null_none" (global (;2;) (shared (ref null (shared none)))))
+  (import "spectest" "global_mut_ref_null_none" (global (;3;) (shared mut (ref null (shared none)))))
+  (import "spectest" "global_ref_none" (global (;4;) (shared (ref (shared none)))))
+  (import "spectest" "global_mut_ref_none" (global (;5;) (shared mut (ref (shared none)))))
+  (global (;6;) (shared (ref null (shared none))) ref.null (shared none))
+  (global (;7;) (shared (ref null (shared none))) ref.null (shared none))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/8.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/8.print
new file mode 100644
index 00000000..293ad9db
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/8.print
@@ -0,0 +1,10 @@
+(module
+  (import "spectest" "global_exnref" (global (;0;) (shared (ref null (shared exn)))))
+  (import "spectest" "global_mut_exnref" (global (;1;) (shared mut (ref null (shared exn)))))
+  (import "spectest" "global_ref_null_exn" (global (;2;) (shared (ref null (shared exn)))))
+  (import "spectest" "global_mut_ref_null_exn" (global (;3;) (shared mut (ref null (shared exn)))))
+  (import "spectest" "global_ref_exn" (global (;4;) (shared (ref (shared exn)))))
+  (import "spectest" "global_mut_ref_exn" (global (;5;) (shared mut (ref (shared exn)))))
+  (global (;6;) (shared (ref null (shared exn))) ref.null (shared exn))
+  (global (;7;) (shared (ref null (shared exn))) ref.null (shared exn))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global.wast.json b/tests/snapshots/local/shared-everything-threads/global.wast.json
index 250e98b1..3d4ab677 100644
--- a/tests/snapshots/local/shared-everything-threads/global.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/global.wast.json
@@ -22,150 +22,150 @@
     },
     {
       "type": "module",
-      "line": 39,
+      "line": 38,
       "filename": "global.3.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 52,
+      "line": 51,
       "filename": "global.4.wasm",
       "text": "global is immutable",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 59,
+      "line": 58,
       "filename": "global.5.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 66,
+      "line": 65,
       "filename": "global.6.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 73,
+      "line": 72,
       "filename": "global.7.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 80,
+      "line": 79,
       "filename": "global.8.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 87,
+      "line": 86,
       "filename": "global.9.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 94,
+      "line": 93,
       "filename": "global.10.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 101,
+      "line": 100,
       "filename": "global.11.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 108,
+      "line": 107,
       "filename": "global.12.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 115,
+      "line": 114,
       "filename": "global.13.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 122,
+      "line": 121,
       "filename": "global.14.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 129,
+      "line": 128,
       "filename": "global.15.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 136,
+      "line": 135,
       "filename": "global.16.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 143,
+      "line": 142,
       "filename": "global.17.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 150,
+      "line": 149,
       "filename": "global.18.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 157,
+      "line": 156,
       "filename": "global.19.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 164,
+      "line": 163,
       "filename": "global.20.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 171,
+      "line": 170,
       "filename": "global.21.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 178,
+      "line": 177,
       "filename": "global.22.wasm",
       "text": "invalid type",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 185,
+      "line": 184,
       "filename": "global.23.wasm"
     },
     {
       "type": "module",
-      "line": 278,
+      "line": 277,
       "filename": "global.24.wasm"
     }
   ]