Repository: bytecodealliance/wasm-tools
Pull request #1646 (2024-06-27 21:09:00)

threads: add `shared` composite types
This continues the work of https://github.com/bytecodealliance/wasm-tools/pull/1600 to expand the surface area of a WebAssembly module that can be marked `shared`. This is for support of the shared-everything-threads [proposal]. The key change is to convert `CompositeType` in each of the crates from an `enum` to a `struct` in order to add a `shared` boolean field. The original variants (`Func`, `Array`, `Struct`) are moved to a separate `enum` and fill an `inner` field. Propagating this throughout the code base is the bulk of this PR, with occasional, small refactors to avoid larger match patterns.

[proposal]: https://github.com/WebAssembly/shared-everything-threads

abrown: cc: @fitzgen 
abrown: Not sure what is wrong in CI here but it seems unrelated to this change.
abrown: Rebased on top of #1651.

Patch:
diff --git a/crates/wasm-encoder/src/core/types.rs b/crates/wasm-encoder/src/core/types.rs
index de6705f9..23859d06 100644
--- a/crates/wasm-encoder/src/core/types.rs
+++ b/crates/wasm-encoder/src/core/types.rs
@@ -25,33 +25,46 @@ impl Encode for SubType {
 
 /// Represents a composite type in a WebAssembly module.
 #[derive(Debug, Clone)]
-pub enum CompositeType {
-    /// The type is for a function.
-    Func(FuncType),
-    /// The type is for an array.
-    Array(ArrayType),
-    /// The type is for a struct.
-    Struct(StructType),
+pub struct CompositeType {
+    /// The type defined inside the composite type.
+    pub inner: CompositeInnerType,
+    /// Whether the type is shared. This is part of the
+    /// shared-everything-threads proposal.
+    pub shared: bool,
 }
 
 impl Encode for CompositeType {
     fn encode(&self, sink: &mut Vec<u8>) {
-        match self {
-            CompositeType::Func(ty) => TypeSection::encode_function(
+        if self.shared {
+            sink.push(0x65);
+        }
+        match &self.inner {
+            CompositeInnerType::Func(ty) => TypeSection::encode_function(
                 sink,
                 ty.params().iter().copied(),
                 ty.results().iter().copied(),
             ),
-            CompositeType::Array(ArrayType(ty)) => {
+            CompositeInnerType::Array(ArrayType(ty)) => {
                 TypeSection::encode_array(sink, &ty.element_type, ty.mutable)
             }
-            CompositeType::Struct(ty) => {
+            CompositeInnerType::Struct(ty) => {
                 TypeSection::encode_struct(sink, ty.fields.iter().cloned())
             }
         }
     }
 }
 
+/// A [`CompositeType`] can contain one of these types.
+#[derive(Debug, Clone)]
+pub enum CompositeInnerType {
+    /// The type is for a function.
+    Func(FuncType),
+    /// The type is for an array.
+    Array(ArrayType),
+    /// The type is for a struct.
+    Struct(StructType),
+}
+
 /// Represents a type of a function in a WebAssembly module.
 #[derive(Debug, Clone, Eq, PartialEq, Hash)]
 pub struct FuncType {
@@ -635,10 +648,9 @@ impl Section for TypeSection {
 
 #[cfg(test)]
 mod tests {
-    use wasmparser::WasmFeatures;
-
     use super::*;
     use crate::Module;
+    use wasmparser::WasmFeatures;
 
     #[test]
     fn func_types_dont_require_wasm_gc() {
@@ -646,7 +658,10 @@ mod tests {
         types.subtype(&SubType {
             is_final: true,
             supertype_idx: None,
-            composite_type: CompositeType::Func(FuncType::new([], [])),
+            composite_type: CompositeType {
+                inner: CompositeInnerType::Func(FuncType::new([], [])),
+                shared: false,
+            },
         });
 
         let mut module = Module::new();
diff --git a/crates/wasm-encoder/src/reencode.rs b/crates/wasm-encoder/src/reencode.rs
index 8624b819..5c6a6711 100644
--- a/crates/wasm-encoder/src/reencode.rs
+++ b/crates/wasm-encoder/src/reencode.rs
@@ -1001,16 +1001,20 @@ pub mod utils {
         reencoder: &mut T,
         composite_ty: wasmparser::CompositeType,
     ) -> Result<crate::CompositeType, Error<T::Error>> {
-        Ok(match composite_ty {
-            wasmparser::CompositeType::Func(f) => {
-                crate::CompositeType::Func(reencoder.func_type(f)?)
+        let inner = match composite_ty.inner {
+            wasmparser::CompositeInnerType::Func(f) => {
+                crate::CompositeInnerType::Func(reencoder.func_type(f)?)
             }
-            wasmparser::CompositeType::Array(a) => {
-                crate::CompositeType::Array(reencoder.array_type(a)?)
+            wasmparser::CompositeInnerType::Array(a) => {
+                crate::CompositeInnerType::Array(reencoder.array_type(a)?)
             }
-            wasmparser::CompositeType::Struct(s) => {
-                crate::CompositeType::Struct(reencoder.struct_type(s)?)
+            wasmparser::CompositeInnerType::Struct(s) => {
+                crate::CompositeInnerType::Struct(reencoder.struct_type(s)?)
             }
+        };
+        Ok(crate::CompositeType {
+            inner,
+            shared: composite_ty.shared,
         })
     }
 
diff --git a/crates/wasm-smith/src/component.rs b/crates/wasm-smith/src/component.rs
index 35bf9323..9428a5f5 100644
--- a/crates/wasm-smith/src/component.rs
+++ b/crates/wasm-smith/src/component.rs
@@ -714,9 +714,12 @@ impl ComponentBuilder {
             });
             let ty_idx = u32::try_from(types.len()).unwrap();
             types.push(realloc_ty.clone());
-            defs.push(ModuleTypeDef::TypeDef(crate::core::CompositeType::Func(
-                realloc_ty.clone(),
-            )));
+            defs.push(ModuleTypeDef::TypeDef(
+                crate::core::CompositeType::new_func(
+                    realloc_ty.clone(),
+                    false, // TODO: handle shared
+                ),
+            ));
             defs.push(ModuleTypeDef::Export(
                 "canonical_abi_realloc".into(),
                 crate::core::EntityType::Func(ty_idx, realloc_ty),
@@ -737,9 +740,12 @@ impl ComponentBuilder {
             });
             let ty_idx = u32::try_from(types.len()).unwrap();
             types.push(free_ty.clone());
-            defs.push(ModuleTypeDef::TypeDef(crate::core::CompositeType::Func(
-                free_ty.clone(),
-            )));
+            defs.push(ModuleTypeDef::TypeDef(
+                crate::core::CompositeType::new_func(
+                    free_ty.clone(),
+                    false, // TODO: handle shared
+                ),
+            ));
             defs.push(ModuleTypeDef::Export(
                 "canonical_abi_free".into(),
                 crate::core::EntityType::Func(ty_idx, free_ty),
@@ -832,7 +838,9 @@ impl ComponentBuilder {
                         0,
                     )?;
                     types.push(ty.clone());
-                    defs.push(ModuleTypeDef::TypeDef(crate::core::CompositeType::Func(ty)));
+                    defs.push(ModuleTypeDef::TypeDef(
+                        crate::core::CompositeType::new_func(ty, false),
+                    )); // TODO: handle shared
                 }
 
                 // Alias
diff --git a/crates/wasm-smith/src/component/encode.rs b/crates/wasm-smith/src/component/encode.rs
index 7963ecfa..3d53f2bf 100644
--- a/crates/wasm-smith/src/component/encode.rs
+++ b/crates/wasm-smith/src/component/encode.rs
@@ -1,5 +1,7 @@
 use std::borrow::Cow;
 
+use crate::core::{CompositeInnerType, CompositeType};
+
 use super::*;
 use wasm_encoder::{ComponentExportKind, ComponentOuterAliasKind, ExportKind};
 
@@ -124,7 +126,10 @@ impl CoreType {
                 let mut enc_mod_ty = wasm_encoder::ModuleType::new();
                 for def in &mod_ty.defs {
                     match def {
-                        ModuleTypeDef::TypeDef(crate::core::CompositeType::Func(func_ty)) => {
+                        ModuleTypeDef::TypeDef(CompositeType {
+                            inner: CompositeInnerType::Func(func_ty),
+                            ..
+                        }) => {
                             enc_mod_ty.ty().function(
                                 func_ty.params.iter().copied(),
                                 func_ty.results.iter().copied(),
diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs
index 6dd32456..43acc72b 100644
--- a/crates/wasm-smith/src/core.rs
+++ b/crates/wasm-smith/src/core.rs
@@ -265,33 +265,62 @@ impl SubType {
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
-pub(crate) enum CompositeType {
-    Array(ArrayType),
-    Func(Rc<FuncType>),
-    Struct(StructType),
+pub(crate) struct CompositeType {
+    pub inner: CompositeInnerType,
+    pub shared: bool,
 }
 
 impl CompositeType {
-    fn unwrap_struct(&self) -> &StructType {
-        match self {
-            CompositeType::Struct(s) => s,
-            _ => panic!("not a struct"),
+    pub(crate) fn new_func(func: Rc<FuncType>, shared: bool) -> Self {
+        Self {
+            inner: CompositeInnerType::Func(func),
+            shared,
         }
     }
 
     fn unwrap_func(&self) -> &Rc<FuncType> {
-        match self {
-            CompositeType::Func(f) => f,
+        match &self.inner {
+            CompositeInnerType::Func(f) => f,
             _ => panic!("not a func"),
         }
     }
 
     fn unwrap_array(&self) -> &ArrayType {
-        match self {
-            CompositeType::Array(a) => a,
+        match &self.inner {
+            CompositeInnerType::Array(a) => a,
             _ => panic!("not an array"),
         }
     }
+
+    fn unwrap_struct(&self) -> &StructType {
+        match &self.inner {
+            CompositeInnerType::Struct(s) => s,
+            _ => panic!("not a struct"),
+        }
+    }
+}
+
+impl From<&CompositeType> for wasm_encoder::CompositeType {
+    fn from(ty: &CompositeType) -> Self {
+        let inner = match &ty.inner {
+            CompositeInnerType::Array(a) => wasm_encoder::CompositeInnerType::Array(a.clone()),
+            CompositeInnerType::Func(f) => wasm_encoder::CompositeInnerType::Func(
+                wasm_encoder::FuncType::new(f.params.iter().cloned(), f.results.iter().cloned()),
+            ),
+            CompositeInnerType::Struct(s) => wasm_encoder::CompositeInnerType::Struct(s.clone()),
+        };
+        wasm_encoder::CompositeType {
+            shared: ty.shared,
+            inner,
+        }
+    }
+}
+
+#[derive(Clone, Debug, PartialEq, Eq, Hash)]
+pub(crate) enum CompositeInnerType {
+    Array(ArrayType),
+    Func(Rc<FuncType>),
+    Struct(StructType),
 }
 
 /// A function signature.
@@ -450,6 +479,7 @@ impl Module {
 
     fn heap_type_is_sub_type(&self, a: HeapType, b: HeapType) -> bool {
         use AbstractHeapType::*;
+        use CompositeInnerType as CT;
         use HeapType as HT;
         match (a, b) {
             (a, b) if a == b => true,
@@ -477,39 +507,27 @@ impl Module {
             }
 
             (HT::Concrete(a), HT::Abstract { shared, ty }) => {
-                if shared {
-                    // TODO: handle shared
-                    todo!("check shared-ness of concrete type");
+                let a_ty = &self.ty(a).composite_type;
+                if a_ty.shared == shared {
+                    return false;
                 }
                 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(_))
-                    }
+                    Eq | Any => matches!(a_ty.inner, CT::Array(_) | CT::Struct(_)),
+                    Struct => matches!(a_ty.inner, CT::Struct(_)),
+                    Array => matches!(a_ty.inner, CT::Array(_)),
+                    Func => matches!(a_ty.inner, CT::Func(_)),
                     _ => false,
                 }
             }
 
             (HT::Abstract { shared, ty }, HT::Concrete(b)) => {
-                if shared {
-                    // TODO: handle shared
-                    todo!("check shared-ness of concrete type");
+                let b_ty = &self.ty(b).composite_type;
+                if shared == b_ty.shared {
+                    return false;
                 }
                 match ty {
-                    None => matches!(
-                        self.ty(b).composite_type,
-                        CompositeType::Array(_) | CompositeType::Struct(_)
-                    ),
-                    NoFunc => matches!(self.ty(b).composite_type, CompositeType::Func(_)),
+                    None => matches!(b_ty.inner, CT::Array(_) | CT::Struct(_)),
+                    NoFunc => matches!(b_ty.inner, CT::Func(_)),
                     _ => false,
                 }
             }
@@ -552,10 +570,10 @@ impl Module {
                 .push(index);
         }
 
-        let list = match &ty.composite_type {
-            CompositeType::Array(_) => &mut self.array_types,
-            CompositeType::Func(_) => &mut self.func_types,
-            CompositeType::Struct(_) => &mut self.struct_types,
+        let list = match &ty.composite_type.inner {
+            CompositeInnerType::Array(_) => &mut self.array_types,
+            CompositeInnerType::Func(_) => &mut self.func_types,
+            CompositeInnerType::Struct(_) => &mut self.struct_types,
         };
         list.push(index);
 
@@ -631,10 +649,14 @@ impl Module {
 
     fn arbitrary_sub_type(&mut self, u: &mut Unstructured) -> Result<SubType> {
         if !self.config.gc_enabled {
+            let composite_type = CompositeType {
+                inner: CompositeInnerType::Func(self.arbitrary_func_type(u)?),
+                shared: false,
+            };
             return Ok(SubType {
                 is_final: true,
                 supertype: None,
-                composite_type: CompositeType::Func(self.arbitrary_func_type(u)?),
+                composite_type,
             });
         }
 
@@ -654,14 +676,14 @@ impl Module {
         let mut composite_type = self.types[usize::try_from(supertype).unwrap()]
             .composite_type
             .clone();
-        match &mut composite_type {
-            CompositeType::Array(a) => {
+        match &mut composite_type.inner {
+            CompositeInnerType::Array(a) => {
                 a.0 = self.arbitrary_matching_field_type(u, a.0)?;
             }
-            CompositeType::Func(f) => {
+            CompositeInnerType::Func(f) => {
                 *f = self.arbitrary_matching_func_type(u, f)?;
             }
-            CompositeType::Struct(s) => {
+            CompositeInnerType::Struct(s) => {
                 *s = self.arbitrary_matching_struct_type(u, s)?;
             }
         }
@@ -739,6 +761,7 @@ impl Module {
         if !self.config.gc_enabled {
             return Ok(ty);
         }
+        use CompositeInnerType as CT;
         use HeapType as HT;
         let mut choices = vec![ty];
         match ty {
@@ -784,16 +807,14 @@ impl Module {
                 match self
                     .types
                     .get(usize::try_from(idx).unwrap())
-                    .map(|ty| &ty.composite_type)
+                    .map(|ty| (ty.composite_type.shared, &ty.composite_type.inner))
                 {
-                    Some(CompositeType::Array(_)) | Some(CompositeType::Struct(_)) => {
-                        choices.push(HT::Abstract {
-                            shared: false, // TODO: handle shared
-                            ty: AbstractHeapType::None,
-                        })
-                    }
-                    Some(CompositeType::Func(_)) => choices.push(HT::Abstract {
-                        shared: false, // TODO: handle shared
+                    Some((shared, CT::Array(_) | CT::Struct(_))) => choices.push(HT::Abstract {
+                        shared,
+                        ty: AbstractHeapType::None,
+                    }),
+                    Some((shared, CT::Func(_))) => choices.push(HT::Abstract {
+                        shared,
                         ty: AbstractHeapType::NoFunc,
                     }),
                     None => {
@@ -868,6 +889,7 @@ impl Module {
         if !self.config.gc_enabled {
             return Ok(ty);
         }
+        use CompositeInnerType as CT;
         use HeapType as HT;
         let mut choices = vec![ty];
         match ty {
@@ -900,38 +922,37 @@ impl Module {
                 }
             }
             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(AbstractHeapType::Any),
-                            ht(AbstractHeapType::Eq),
-                            ht(AbstractHeapType::Array),
-                        ]);
-                    }
-                    Some(CompositeType::Func(_)) => {
-                        choices.push(ht(AbstractHeapType::Func));
-                    }
-                    Some(CompositeType::Struct(_)) => {
-                        choices.extend([
-                            ht(AbstractHeapType::Any),
-                            ht(AbstractHeapType::Eq),
-                            ht(AbstractHeapType::Struct),
-                        ]);
-                    }
-                    None => {
-                        // Same as in `arbitrary_matching_heap_type`: this was a
-                        // forward reference to a concrete type that is part of
-                        // this same rec group we are generating right now, and
-                        // therefore we haven't generated that type yet. Just
-                        // leave `choices` as it is and we will choose the
-                        // original type again down below.
+                if let Some(sub_ty) = &self.types.get(usize::try_from(idx).unwrap()) {
+                    let ht = |ty| HT::Abstract {
+                        shared: sub_ty.composite_type.shared,
+                        ty,
+                    };
+                    match &sub_ty.composite_type.inner {
+                        CT::Array(_) => {
+                            choices.extend([
+                                ht(AbstractHeapType::Any),
+                                ht(AbstractHeapType::Eq),
+                                ht(AbstractHeapType::Array),
+                            ]);
+                        }
+                        CT::Func(_) => {
+                            choices.push(ht(AbstractHeapType::Func));
+                        }
+                        CT::Struct(_) => {
+                            choices.extend([
+                                ht(AbstractHeapType::Any),
+                                ht(AbstractHeapType::Eq),
+                                ht(AbstractHeapType::Struct),
+                            ]);
+                        }
                     }
+                } else {
+                    // Same as in `arbitrary_matching_heap_type`: this was a
+                    // forward reference to a concrete type that is part of
+                    // this same rec group we are generating right now, and
+                    // therefore we haven't generated that type yet. Just
+                    // leave `choices` as it is and we will choose the
+                    // original type again down below.
                 }
                 while let Some(supertype) = self
                     .types
@@ -947,16 +968,28 @@ impl Module {
     }
 
     fn arbitrary_composite_type(&mut self, u: &mut Unstructured) -> Result<CompositeType> {
+        use CompositeInnerType as CT;
+        let shared = false; // TODO: handle shared
         if !self.config.gc_enabled {
-            return Ok(CompositeType::Func(self.arbitrary_func_type(u)?));
+            return Ok(CompositeType {
+                shared,
+                inner: CT::Func(self.arbitrary_func_type(u)?),
+            });
         }
 
         match u.int_in_range(0..=2)? {
-            0 => Ok(CompositeType::Array(ArrayType(
-                self.arbitrary_field_type(u)?,
-            ))),
-            1 => Ok(CompositeType::Func(self.arbitrary_func_type(u)?)),
-            2 => Ok(CompositeType::Struct(self.arbitrary_struct_type(u)?)),
+            0 => Ok(CompositeType {
+                shared,
+                inner: CT::Array(ArrayType(self.arbitrary_field_type(u)?)),
+            }),
+            1 => Ok(CompositeType {
+                shared,
+                inner: CT::Func(self.arbitrary_func_type(u)?),
+            }),
+            2 => Ok(CompositeType {
+                shared,
+                inner: CT::Struct(self.arbitrary_struct_type(u)?),
+            }),
             _ => unreachable!(),
         }
     }
@@ -1266,13 +1299,16 @@ impl Module {
                     new_types.push(SubType {
                         is_final: true,
                         supertype: None,
-                        composite_type: CompositeType::Func(Rc::clone(&func_type)),
+                        composite_type: CompositeType::new_func(Rc::clone(&func_type), false), // TODO: handle shared
                     });
                     new_index
                 }
             };
-            match &new_types[serialized_sig_idx - first_type_index].composite_type {
-                CompositeType::Func(f) => Some((serialized_sig_idx as u32, Rc::clone(f))),
+            match &new_types[serialized_sig_idx - first_type_index]
+                .composite_type
+                .inner
+            {
+                CompositeInnerType::Func(f) => Some((serialized_sig_idx as u32, Rc::clone(f))),
                 _ => unimplemented!(),
             }
         };
@@ -1394,8 +1430,8 @@ impl Module {
     }
 
     fn func_type(&self, idx: u32) -> &Rc<FuncType> {
-        match &self.ty(idx).composite_type {
-            CompositeType::Func(f) => f,
+        match &self.ty(idx).composite_type.inner {
+            CompositeInnerType::Func(f) => f,
             _ => panic!("types[{idx}] is not a func type"),
         }
     }
@@ -1708,8 +1744,8 @@ impl Module {
                             id
                         )
                     });
-                    match &subtype.composite_type {
-                        wasmparser::CompositeType::Func(func_type) => {
+                    match &subtype.composite_type.inner {
+                        wasmparser::CompositeInnerType::Func(func_type) => {
                             assert!(
                                 subtype.is_final,
                                 "Subtype {:?} from `exports` Wasm is not final",
@@ -1738,7 +1774,10 @@ impl Module {
                             let type_index = self.add_type(SubType {
                                 is_final: true,
                                 supertype: None,
-                                composite_type: CompositeType::Func(Rc::clone(&new_type)),
+                                composite_type: CompositeType::new_func(
+                                    Rc::clone(&new_type),
+                                    false,
+                                ), // TODO: handle shared
                             });
                             let func_index = self.funcs.len() as u32;
                             self.funcs.push((type_index, new_type));
diff --git a/crates/wasm-smith/src/core/code_builder.rs b/crates/wasm-smith/src/core/code_builder.rs
index 97fdaa36..f06340ae 100644
--- a/crates/wasm-smith/src/core/code_builder.rs
+++ b/crates/wasm-smith/src/core/code_builder.rs
@@ -1,6 +1,6 @@
 use super::{
-    CompositeType, Elements, FuncType, Instruction, InstructionKind::*, InstructionKinds, Module,
-    ValType,
+    CompositeInnerType, Elements, FuncType, Instruction, InstructionKind::*, InstructionKinds,
+    Module, ValType,
 };
 use crate::{unique_string, MemoryOffsetChoices};
 use arbitrary::{Result, Unstructured};
@@ -1145,8 +1145,8 @@ impl CodeBuilder<'_> {
         at: usize,
     ) -> Option<(bool, u32, ArrayType)> {
         let (nullable, ty) = self.concrete_ref_type_on_stack_at(at)?;
-        match &module.ty(ty).composite_type {
-            CompositeType::Array(a) => Some((nullable, ty, *a)),
+        match &module.ty(ty).composite_type.inner {
+            CompositeInnerType::Array(a) => Some((nullable, ty, *a)),
             _ => None,
         }
     }
@@ -1159,8 +1159,8 @@ impl CodeBuilder<'_> {
         at: usize,
     ) -> Option<(bool, u32, &'a StructType)> {
         let (nullable, ty) = self.concrete_ref_type_on_stack_at(at)?;
-        match &module.ty(ty).composite_type {
-            CompositeType::Struct(s) => Some((nullable, ty, s)),
+        match &module.ty(ty).composite_type.inner {
+            CompositeInnerType::Struct(s) => Some((nullable, ty, s)),
             _ => None,
         }
     }
@@ -1189,9 +1189,9 @@ impl CodeBuilder<'_> {
     fn concrete_funcref_on_stack(&self, module: &Module) -> Option<RefType> {
         match self.operands().last().copied()?? {
             ValType::Ref(r) => match r.heap_type {
-                HeapType::Concrete(idx) => match &module.ty(idx).composite_type {
-                    CompositeType::Func(_) => Some(r),
-                    CompositeType::Struct(_) | CompositeType::Array(_) => None,
+                HeapType::Concrete(idx) => match &module.ty(idx).composite_type.inner {
+                    CompositeInnerType::Func(_) => Some(r),
+                    CompositeInnerType::Struct(_) | CompositeInnerType::Array(_) => None,
                 },
                 _ => None,
             },
@@ -1206,8 +1206,10 @@ impl CodeBuilder<'_> {
             Some(Some(ValType::Ref(RefType {
                 nullable,
                 heap_type: HeapType::Concrete(idx),
-            }))) => match &module.ty(*idx).composite_type {
-                CompositeType::Struct(s) => !s.fields.is_empty() && (!nullable || allow_null_refs),
+            }))) => match &module.ty(*idx).composite_type.inner {
+                CompositeInnerType::Struct(s) => {
+                    !s.fields.is_empty() && (!nullable || allow_null_refs)
+                }
                 _ => false,
             },
             _ => false,
@@ -2032,8 +2034,8 @@ fn call_ref(
         HeapType::Concrete(idx) => idx,
         _ => unreachable!(),
     };
-    let func_ty = match &module.ty(idx).composite_type {
-        CompositeType::Func(f) => f,
+    let func_ty = match &module.ty(idx).composite_type.inner {
+        CompositeInnerType::Func(f) => f,
         _ => unreachable!(),
     };
     builder.pop_operands(module, &func_ty.params);
@@ -2165,9 +2167,9 @@ fn return_call_ref_valid(module: &Module, builder: &mut CodeBuilder) -> bool {
         HeapType::Concrete(idx) => idx,
         _ => unreachable!(),
     };
-    let func_ty = match &module.ty(idx).composite_type {
-        CompositeType::Func(f) => f,
-        CompositeType::Array(_) | CompositeType::Struct(_) => return false,
+    let func_ty = match &module.ty(idx).composite_type.inner {
+        CompositeInnerType::Func(f) => f,
+        CompositeInnerType::Array(_) | CompositeInnerType::Struct(_) => return false,
     };
 
     let ty = builder.allocs.operands.pop().unwrap();
@@ -2191,8 +2193,8 @@ fn return_call_ref(
         HeapType::Concrete(idx) => idx,
         _ => unreachable!(),
     };
-    let func_ty = match &module.ty(idx).composite_type {
-        CompositeType::Func(f) => f,
+    let func_ty = match &module.ty(idx).composite_type.inner {
+        CompositeInnerType::Func(f) => f,
         _ => unreachable!(),
     };
     builder.pop_operands(module, &func_ty.params);
diff --git a/crates/wasm-smith/src/core/encode.rs b/crates/wasm-smith/src/core/encode.rs
index 5a661509..fc408e9c 100644
--- a/crates/wasm-smith/src/core/encode.rs
+++ b/crates/wasm-smith/src/core/encode.rs
@@ -39,16 +39,7 @@ impl Module {
                 section.subtype(&wasm_encoder::SubType {
                     is_final: ty.is_final,
                     supertype_idx: ty.supertype,
-                    composite_type: match &ty.composite_type {
-                        CompositeType::Array(a) => wasm_encoder::CompositeType::Array(a.clone()),
-                        CompositeType::Func(f) => {
-                            wasm_encoder::CompositeType::Func(wasm_encoder::FuncType::new(
-                                f.params.iter().cloned(),
-                                f.results.iter().cloned(),
-                            ))
-                        }
-                        CompositeType::Struct(s) => wasm_encoder::CompositeType::Struct(s.clone()),
-                    },
+                    composite_type: (&ty.composite_type).into(),
                 });
             } else {
                 section.rec(
@@ -57,20 +48,7 @@ impl Module {
                         .map(|ty| wasm_encoder::SubType {
                             is_final: ty.is_final,
                             supertype_idx: ty.supertype,
-                            composite_type: match &ty.composite_type {
-                                CompositeType::Array(a) => {
-                                    wasm_encoder::CompositeType::Array(a.clone())
-                                }
-                                CompositeType::Func(f) => {
-                                    wasm_encoder::CompositeType::Func(wasm_encoder::FuncType::new(
-                                        f.params.iter().cloned(),
-                                        f.results.iter().cloned(),
-                                    ))
-                                }
-                                CompositeType::Struct(s) => {
-                                    wasm_encoder::CompositeType::Struct(s.clone())
-                                }
-                            },
+                            composite_type: (&ty.composite_type).into(),
                         }),
                 );
             }
diff --git a/crates/wasmparser/src/readers/core/types.rs b/crates/wasmparser/src/readers/core/types.rs
index eb1b253d..f85ca093 100644
--- a/crates/wasmparser/src/readers/core/types.rs
+++ b/crates/wasmparser/src/readers/core/types.rs
@@ -503,8 +503,8 @@ impl SubType {
         if let Some(idx) = &mut self.supertype_idx {
             f(idx)?;
         }
-        match &mut self.composite_type {
-            CompositeType::Func(ty) => {
+        match &mut self.composite_type.inner {
+            CompositeInnerType::Func(ty) => {
                 for ty in ty.params_mut() {
                     ty.remap_indices(f)?;
                 }
@@ -512,10 +512,10 @@ impl SubType {
                     ty.remap_indices(f)?;
                 }
             }
-            CompositeType::Array(ty) => {
+            CompositeInnerType::Array(ty) => {
                 ty.0.remap_indices(f)?;
             }
-            CompositeType::Struct(ty) => {
+            CompositeInnerType::Struct(ty) => {
                 for field in ty.fields.iter_mut() {
                     field.remap_indices(f)?;
                 }
@@ -527,7 +527,17 @@ impl SubType {
 
 /// Represents a composite type in a WebAssembly module.
 #[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
-pub enum CompositeType {
+pub struct CompositeType {
+    /// The type defined inside the composite type.
+    pub inner: CompositeInnerType,
+    /// Is the composite type shared? This is part of the
+    /// shared-everything-threads proposal.
+    pub shared: bool,
+}
+
+/// A [`CompositeType`] can contain one of these types.
+#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
+pub enum CompositeInnerType {
     /// The type is for a function.
     Func(FuncType),
     /// The type is for an array.
@@ -538,43 +548,46 @@ pub enum CompositeType {
 
 impl fmt::Display for CompositeType {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match *self {
-            Self::Array(_) => write!(f, "(array ...)"),
-            Self::Func(_) => write!(f, "(func ...)"),
-            Self::Struct(_) => write!(f, "(struct ...)"),
+        use CompositeInnerType::*;
+        if self.shared {
+            write!(f, "(shared ")?;
+        }
+        match self.inner {
+            Array(_) => write!(f, "(array ...)"),
+            Func(_) => write!(f, "(func ...)"),
+            Struct(_) => write!(f, "(struct ...)"),
+        }?;
+        if self.shared {
+            write!(f, ")")?;
         }
+        Ok(())
     }
 }
 
 impl CompositeType {
     /// Unwrap a `FuncType` or panic.
     pub fn unwrap_func(&self) -> &FuncType {
-        match self {
-            Self::Func(f) => f,
+        match &self.inner {
+            CompositeInnerType::Func(f) => f,
             _ => panic!("not a func"),
         }
     }
 
     /// Unwrap a `ArrayType` or panic.
     pub fn unwrap_array(&self) -> &ArrayType {
-        match self {
-            Self::Array(a) => a,
+        match &self.inner {
+            CompositeInnerType::Array(a) => a,
             _ => panic!("not a array"),
         }
     }
 
     /// Unwrap a `StructType` or panic.
     pub fn unwrap_struct(&self) -> &StructType {
-        match self {
-            Self::Struct(s) => s,
+        match &self.inner {
+            CompositeInnerType::Struct(s) => s,
             _ => 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.
@@ -1796,9 +1809,9 @@ impl<'a> TypeSectionReader<'a> {
             if !ty.is_final || ty.supertype_idx.is_some() {
                 bail!(offset, "gc proposal not supported");
             }
-            match ty.composite_type {
-                CompositeType::Func(f) => Ok(f),
-                CompositeType::Array(_) | CompositeType::Struct(_) => {
+            match ty.composite_type.inner {
+                CompositeInnerType::Func(f) => Ok(f),
+                CompositeInnerType::Array(_) | CompositeInnerType::Struct(_) => {
                     bail!(offset, "gc proposal not supported");
                 }
             }
@@ -1818,12 +1831,18 @@ fn read_composite_type(
 ) -> Result<CompositeType, BinaryReaderError> {
     // NB: See `FromReader<'a> for ValType` for a table of how this
     // interacts with other value encodings.
-    Ok(match opcode {
-        0x60 => CompositeType::Func(reader.read()?),
-        0x5e => CompositeType::Array(reader.read()?),
-        0x5f => CompositeType::Struct(reader.read()?),
+    let (shared, opcode) = if opcode == 0x65 {
+        (true, reader.read_u8()?)
+    } else {
+        (false, opcode)
+    };
+    let inner = match opcode {
+        0x60 => CompositeInnerType::Func(reader.read()?),
+        0x5e => CompositeInnerType::Array(reader.read()?),
+        0x5f => CompositeInnerType::Struct(reader.read()?),
         x => return reader.invalid_leading_byte(x, "type"),
-    })
+    };
+    Ok(CompositeType { shared, inner })
 }
 
 impl<'a> FromReader<'a> for RecGroup {
diff --git a/crates/wasmparser/src/readers/core/types/matches.rs b/crates/wasmparser/src/readers/core/types/matches.rs
index 5976b305..fee15966 100644
--- a/crates/wasmparser/src/readers/core/types/matches.rs
+++ b/crates/wasmparser/src/readers/core/types/matches.rs
@@ -14,8 +14,8 @@
 
 use crate::{
     types::{CoreTypeId, RecGroupId, TypeList},
-    ArrayType, CompositeType, FieldType, FuncType, RefType, StorageType, StructType, SubType,
-    ValType,
+    ArrayType, CompositeInnerType, CompositeType, FieldType, FuncType, RefType, StorageType,
+    StructType, SubType, ValType,
 };
 
 /// Wasm type matching.
@@ -103,27 +103,31 @@ impl<'a> Matches for WithRecGroup<&'a SubType> {
 
 impl<'a> Matches for WithRecGroup<&'a CompositeType> {
     fn matches(types: &TypeList, a: Self, b: Self) -> bool {
-        match (&*a, &*b) {
-            (CompositeType::Func(fa), CompositeType::Func(fb)) => Matches::matches(
+        use CompositeInnerType::*;
+        if (*a).shared != (*b).shared {
+            return false;
+        }
+        match (&(*a).inner, &(*b).inner) {
+            (Func(fa), Func(fb)) => Matches::matches(
                 types,
                 WithRecGroup::map(a, |_| fa),
                 WithRecGroup::map(b, |_| fb),
             ),
-            (CompositeType::Func(_), _) => false,
+            (Func(_), _) => false,
 
-            (CompositeType::Array(aa), CompositeType::Array(ab)) => Matches::matches(
+            (Array(aa), Array(ab)) => Matches::matches(
                 types,
                 WithRecGroup::map(a, |_| *aa),
                 WithRecGroup::map(b, |_| *ab),
             ),
-            (CompositeType::Array(_), _) => false,
+            (Array(_), _) => false,
 
-            (CompositeType::Struct(sa), CompositeType::Struct(sb)) => Matches::matches(
+            (Struct(sa), Struct(sb)) => Matches::matches(
                 types,
                 WithRecGroup::map(a, |_| sa),
                 WithRecGroup::map(b, |_| sb),
             ),
-            (CompositeType::Struct(_), _) => false,
+            (Struct(_), _) => false,
         }
     }
 }
diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs
index 80dbd2e3..56658af7 100644
--- a/crates/wasmparser/src/validator/component.rs
+++ b/crates/wasmparser/src/validator/component.rs
@@ -10,9 +10,9 @@ use super::{
         ModuleType, RecordType, Remapping, ResourceId, TypeAlloc, TypeList, VariantCase,
     },
 };
-use crate::collections::index_map::Entry;
 use crate::prelude::*;
 use crate::validator::names::{ComponentName, ComponentNameKind, KebabStr, KebabString};
+use crate::{collections::index_map::Entry, CompositeInnerType};
 use crate::{
     limits::*,
     types::{
@@ -1005,10 +1005,14 @@ impl ComponentState {
 
         self.check_options(None, &info, &options, types, offset)?;
 
+        let composite_type = CompositeType {
+            inner: CompositeInnerType::Func(info.into_func_type()),
+            shared: false,
+        };
         let lowered_ty = SubType {
             is_final: true,
             supertype_idx: None,
-            composite_type: CompositeType::Func(info.into_func_type()),
+            composite_type,
         };
 
         let (_is_new, group_id) =
@@ -1026,10 +1030,14 @@ impl ComponentState {
         offset: usize,
     ) -> Result<()> {
         let rep = self.check_local_resource(resource, types, offset)?;
+        let composite_type = CompositeType {
+            inner: CompositeInnerType::Func(FuncType::new([rep], [ValType::I32])),
+            shared: false,
+        };
         let core_ty = SubType {
             is_final: true,
             supertype_idx: None,
-            composite_type: CompositeType::Func(FuncType::new([rep], [ValType::I32])),
+            composite_type,
         };
         let (_is_new, group_id) =
             types.intern_canonical_rec_group(RecGroup::implicit(offset, core_ty));
@@ -1045,10 +1053,14 @@ impl ComponentState {
         offset: usize,
     ) -> Result<()> {
         self.resource_at(resource, types, offset)?;
+        let composite_type = CompositeType {
+            inner: CompositeInnerType::Func(FuncType::new([ValType::I32], [])),
+            shared: false,
+        };
         let core_ty = SubType {
             is_final: true,
             supertype_idx: None,
-            composite_type: CompositeType::Func(FuncType::new([ValType::I32], [])),
+            composite_type,
         };
         let (_is_new, group_id) =
             types.intern_canonical_rec_group(RecGroup::implicit(offset, core_ty));
@@ -1064,10 +1076,14 @@ impl ComponentState {
         offset: usize,
     ) -> Result<()> {
         let rep = self.check_local_resource(resource, types, offset)?;
+        let composite_type = CompositeType {
+            inner: CompositeInnerType::Func(FuncType::new([ValType::I32], [rep])),
+            shared: false,
+        };
         let core_ty = SubType {
             is_final: true,
             supertype_idx: None,
-            composite_type: CompositeType::Func(FuncType::new([ValType::I32], [rep])),
+            composite_type,
         };
         let (_is_new, group_id) =
             types.intern_canonical_rec_group(RecGroup::implicit(offset, core_ty));
diff --git a/crates/wasmparser/src/validator/core.rs b/crates/wasmparser/src/validator/core.rs
index 173a74d8..4c78eaf4 100644
--- a/crates/wasmparser/src/validator/core.rs
+++ b/crates/wasmparser/src/validator/core.rs
@@ -9,7 +9,6 @@ use super::{
     operators::{ty_to_str, OperatorValidator, OperatorValidatorAllocations},
     types::{CoreTypeId, EntityType, RecGroupId, TypeAlloc, TypeList},
 };
-use crate::prelude::*;
 use crate::{
     limits::*, validator::types::TypeIdentifier, BinaryReaderError, CompositeType, ConstExpr, Data,
     DataKind, Element, ElementKind, ExternalKind, FuncType, Global, GlobalType, HeapType,
@@ -17,6 +16,7 @@ use crate::{
     TableType, TagType, TypeRef, UnpackedIndex, ValType, VisitOperator, WasmFeatures,
     WasmModuleResources,
 };
+use crate::{prelude::*, CompositeInnerType};
 use alloc::sync::Arc;
 use core::mem;
 
@@ -608,7 +608,7 @@ impl Module {
             bail!(offset, "gc proposal must be enabled to use subtypes");
         }
 
-        self.check_composite_type(&ty.composite_type, features, offset)?;
+        self.check_composite_type(&ty.composite_type, features, &types, offset)?;
 
         let depth = if let Some(supertype_index) = ty.supertype_idx {
             debug_assert!(supertype_index.is_canonical());
@@ -641,17 +641,36 @@ impl Module {
         &mut self,
         ty: &CompositeType,
         features: &WasmFeatures,
+        types: &TypeList,
         offset: usize,
     ) -> Result<()> {
-        let check = |ty: &ValType| {
+        let check = |ty: &ValType, shared: bool| {
             features
                 .check_value_type(*ty)
-                .map_err(|e| BinaryReaderError::new(e, offset))
+                .map_err(|e| BinaryReaderError::new(e, offset))?;
+            if shared && !types.valtype_is_shared(*ty) {
+                return Err(BinaryReaderError::new(
+                    "shared composite type must contain shared types",
+                    offset,
+                ));
+                // The other cases are fine:
+                // - both shared or unshared: good to go
+                // - the func type is unshared, `ty` is shared: though
+                //   odd, we _can_ in fact use shared values in
+                //   unshared composite types (e.g., functions).
+            }
+            Ok(())
         };
-        match ty {
-            CompositeType::Func(t) => {
-                for ty in t.params().iter().chain(t.results()) {
-                    check(ty)?;
+        if !features.shared_everything_threads() && ty.shared {
+            return Err(BinaryReaderError::new(
+                "shared composite types are not supported without the shared-everything-threads feature",
+                offset,
+            ));
+        }
+        match &ty.inner {
+            CompositeInnerType::Func(t) => {
+                for vt in t.params().iter().chain(t.results()) {
+                    check(vt, ty.shared)?;
                 }
                 if t.results().len() > 1 && !features.multi_value() {
                     return Err(BinaryReaderError::new(
@@ -660,7 +679,7 @@ impl Module {
                     ));
                 }
             }
-            CompositeType::Array(t) => {
+            CompositeInnerType::Array(t) => {
                 if !features.gc() {
                     return Err(BinaryReaderError::new(
                         "array indexed types not supported without the gc feature",
@@ -668,21 +687,25 @@ impl Module {
                     ));
                 }
                 match &t.0.element_type {
-                    StorageType::I8 | StorageType::I16 => {}
-                    StorageType::Val(value_type) => check(value_type)?,
+                    StorageType::I8 | StorageType::I16 => {
+                        // Note: scalar types are always `shared`.
+                    }
+                    StorageType::Val(value_type) => check(value_type, ty.shared)?,
                 };
             }
-            CompositeType::Struct(t) => {
+            CompositeInnerType::Struct(t) => {
                 if !features.gc() {
                     return Err(BinaryReaderError::new(
                         "struct indexed types not supported without the gc feature",
                         offset,
                     ));
                 }
-                for ty in t.fields.iter() {
-                    match &ty.element_type {
-                        StorageType::I8 | StorageType::I16 => {}
-                        StorageType::Val(value_type) => check(value_type)?,
+                for ft in t.fields.iter() {
+                    match &ft.element_type {
+                        StorageType::I8 | StorageType::I16 => {
+                            // Note: scalar types are always `shared`.
+                        }
+                        StorageType::Val(value_type) => check(value_type, ty.shared)?,
                     }
                 }
             }
@@ -824,8 +847,12 @@ impl Module {
         types: &'a TypeList,
         offset: usize,
     ) -> Result<&'a FuncType> {
-        match &self.sub_type_at(types, type_index, offset)?.composite_type {
-            CompositeType::Func(f) => Ok(f),
+        match &self
+            .sub_type_at(types, type_index, offset)?
+            .composite_type
+            .inner
+        {
+            CompositeInnerType::Func(f) => Ok(f),
             _ => bail!(offset, "type index {type_index} is not a function type"),
         }
     }
@@ -1307,8 +1334,8 @@ impl WasmModuleResources for ValidatorResources {
     fn tag_at(&self, at: u32) -> Option<&FuncType> {
         let id = *self.0.tags.get(at as usize)?;
         let types = self.0.snapshot.as_ref().unwrap();
-        match &types[id].composite_type {
-            CompositeType::Func(f) => Some(f),
+        match &types[id].composite_type.inner {
+            CompositeInnerType::Func(f) => Some(f),
             _ => None,
         }
     }
diff --git a/crates/wasmparser/src/validator/func.rs b/crates/wasmparser/src/validator/func.rs
index 0c482574..9d393585 100644
--- a/crates/wasmparser/src/validator/func.rs
+++ b/crates/wasmparser/src/validator/func.rs
@@ -246,7 +246,10 @@ mod tests {
             EmptyResources(crate::SubType {
                 supertype_idx: None,
                 is_final: true,
-                composite_type: crate::CompositeType::Func(crate::FuncType::new([], [])),
+                composite_type: crate::CompositeType {
+                    inner: crate::CompositeInnerType::Func(crate::FuncType::new([], [])),
+                    shared: false,
+                },
             })
         }
     }
diff --git a/crates/wasmparser/src/validator/operators.rs b/crates/wasmparser/src/validator/operators.rs
index 52dceebb..e0f2c7ae 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, 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,
+    BrTable, Catch, FieldType, FuncType, GlobalType, HeapType, Ieee32, Ieee64, MemArg, RefType,
+    Result, StorageType, StructType, SubType, TableType, TryTable, UnpackedIndex, ValType,
+    VisitOperator, WasmFeatures, WasmModuleResources, V128,
 };
+use crate::{prelude::*, CompositeInnerType};
 use core::ops::{Deref, DerefMut};
 
 pub(crate) struct OperatorValidator {
@@ -1174,7 +1174,7 @@ where
 
     fn struct_type_at(&self, at: u32) -> Result<&'resources StructType> {
         let sub_ty = self.sub_type_at(at)?;
-        if let CompositeType::Struct(struct_ty) = &sub_ty.composite_type {
+        if let CompositeInnerType::Struct(struct_ty) = &sub_ty.composite_type.inner {
             Ok(struct_ty)
         } else {
             bail!(
@@ -1199,7 +1199,7 @@ where
 
     fn array_type_at(&self, at: u32) -> Result<&'resources ArrayType> {
         let sub_ty = self.sub_type_at(at)?;
-        if let CompositeType::Array(array_ty) = &sub_ty.composite_type {
+        if let CompositeInnerType::Array(array_ty) = &sub_ty.composite_type.inner {
             Ok(array_ty)
         } else {
             bail!(
@@ -1211,7 +1211,7 @@ where
 
     fn func_type_at(&self, at: u32) -> Result<&'resources FuncType> {
         let sub_ty = self.sub_type_at(at)?;
-        if let CompositeType::Func(func_ty) = &sub_ty.composite_type {
+        if let CompositeInnerType::Func(func_ty) = &sub_ty.composite_type.inner {
             Ok(func_ty)
         } else {
             bail!(
diff --git a/crates/wasmparser/src/validator/types.rs b/crates/wasmparser/src/validator/types.rs
index 6e4f452e..578ba8b2 100644
--- a/crates/wasmparser/src/validator/types.rs
+++ b/crates/wasmparser/src/validator/types.rs
@@ -4,13 +4,13 @@ use super::{
     component::{ComponentState, ExternKind},
     core::Module,
 };
-use crate::prelude::*;
 use crate::{collections::map::Entry, AbstractHeapType};
+use crate::{prelude::*, CompositeInnerType};
 use crate::{validator::names::KebabString, HeapType, ValidatorId};
 use crate::{
-    BinaryReaderError, CompositeType, Export, ExternalKind, FuncType, GlobalType, Import, Matches,
-    MemoryType, PackedIndex, PrimitiveValType, RecGroup, RefType, Result, SubType, TableType,
-    TypeRef, UnpackedIndex, ValType, WithRecGroup,
+    BinaryReaderError, Export, ExternalKind, FuncType, GlobalType, Import, Matches, MemoryType,
+    PackedIndex, PrimitiveValType, RecGroup, RefType, Result, SubType, TableType, TypeRef,
+    UnpackedIndex, ValType, WithRecGroup,
 };
 use alloc::sync::Arc;
 use core::ops::{Deref, DerefMut, Index, Range};
@@ -293,11 +293,12 @@ impl TypeData for SubType {
 
     fn type_info(&self, _types: &TypeList) -> TypeInfo {
         // TODO(#1036): calculate actual size for func, array, struct.
-        let size = 1 + match &self.composite_type {
-            CompositeType::Func(ty) => 1 + (ty.params().len() + ty.results().len()) as u32,
-            CompositeType::Array(_) => 2,
-            CompositeType::Struct(ty) => 1 + 2 * ty.fields.len() as u32,
+        let size = 1 + match &self.composite_type.inner {
+            CompositeInnerType::Func(ty) => 1 + (ty.params().len() + ty.results().len()) as u32,
+            CompositeInnerType::Array(_) => 2,
+            CompositeInnerType::Struct(ty) => 1 + 2 * ty.fields.len() as u32,
         };
+        // TODO: handle shared?
         TypeInfo::core(size)
     }
 }
@@ -313,9 +314,9 @@ impl CoreType {
 
     /// Get the underlying `FuncType` within this `SubType` or panic.
     pub fn unwrap_func(&self) -> &FuncType {
-        match &self.unwrap_sub().composite_type {
-            CompositeType::Func(f) => f,
-            CompositeType::Array(_) | CompositeType::Struct(_) => {
+        match &self.unwrap_sub().composite_type.inner {
+            CompositeInnerType::Func(f) => f,
+            CompositeInnerType::Array(_) | CompositeInnerType::Struct(_) => {
                 panic!("`unwrap_func` on non-func composite type")
             }
         }
@@ -2744,6 +2745,7 @@ impl TypeList {
         };
 
         use AbstractHeapType::*;
+        use CompositeInnerType as CT;
         use HeapType as HT;
         match (a.heap_type(), b.heap_type()) {
             (a, b) if a == b => true,
@@ -2778,24 +2780,15 @@ impl TypeList {
             }
 
             (HT::Concrete(a), HT::Abstract { shared, ty }) => {
-                if shared {
-                    // TODO: handle shared
-                    todo!("check shared-ness of concrete type");
+                let a_ty = &subtype(a_group, a).composite_type;
+                if a_ty.shared != shared {
+                    return false;
                 }
                 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(_))
-                    }
+                    Any | Eq => matches!(a_ty.inner, CT::Array(_) | CT::Struct(_)),
+                    Struct => matches!(a_ty.inner, CT::Struct(_)),
+                    Array => matches!(a_ty.inner, CT::Array(_)),
+                    Func => matches!(a_ty.inner, CT::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,
@@ -2803,16 +2796,13 @@ impl TypeList {
             }
 
             (HT::Abstract { shared, ty }, HT::Concrete(b)) => {
-                if shared {
-                    // TODO: handle shared
-                    todo!("check shared-ness of concrete type");
+                let b_ty = &subtype(b_group, b).composite_type;
+                if shared != b_ty.shared {
+                    return false;
                 }
                 match ty {
-                    None => matches!(
-                        subtype(b_group, b).composite_type,
-                        CompositeType::Array(_) | CompositeType::Struct(_)
-                    ),
-                    NoFunc => matches!(subtype(b_group, b).composite_type, CompositeType::Func(_)),
+                    None => matches!(b_ty.inner, CT::Array(_) | CT::Struct(_)),
+                    NoFunc => matches!(b_ty.inner, CT::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 => {
@@ -2853,9 +2843,9 @@ impl TypeList {
             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(),
+                HeapType::Concrete(index) => {
+                    self[index.as_core_type_id().unwrap()].composite_type.shared
+                }
             },
         }
     }
@@ -2867,16 +2857,16 @@ impl TypeList {
     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::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::Concrete(idx) => {
+                let ty = &self[idx.as_core_type_id().unwrap()].composite_type;
+                let shared = ty.shared;
+                match ty.inner {
+                    CompositeInnerType::Func(_) => HeapType::Abstract { shared, ty: Func },
+                    CompositeInnerType::Array(_) | CompositeInnerType::Struct(_) => {
+                        HeapType::Abstract { shared, ty: Any }
+                    }
+                }
+            }
             HeapType::Abstract { shared, ty } => {
                 let ty = match ty {
                     Func | NoFunc => Func,
diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs
index a6dd2575..6c6d2943 100644
--- a/crates/wasmprinter/src/lib.rs
+++ b/crates/wasmprinter/src/lib.rs
@@ -751,9 +751,9 @@ impl Printer<'_, '_> {
         )?;
         let ty = match ty {
             CoreType::Sub(ty) => {
-                let ty = match &ty.composite_type {
-                    CompositeType::Func(f) => f,
-                    CompositeType::Array(_) | CompositeType::Struct(_) => {
+                let ty = match &ty.composite_type.inner {
+                    CompositeInnerType::Func(f) => f,
+                    CompositeInnerType::Array(_) | CompositeInnerType::Struct(_) => {
                         unreachable!("Wasm GC types cannot appear in components yet")
                     }
                 };
@@ -761,10 +761,14 @@ impl Printer<'_, '_> {
                 self.start_group("func")?;
                 self.print_func_type(states.last().unwrap(), &ty, None)?;
                 self.end_group()?;
+                let composite_type = CompositeType {
+                    inner: CompositeInnerType::Func(ty.clone()),
+                    shared: false,
+                };
                 Some(SubType {
                     is_final: true,
                     supertype_idx: None,
-                    composite_type: CompositeType::Func(ty.clone()),
+                    composite_type,
                 })
             }
             CoreType::Module(decls) => {
@@ -818,26 +822,32 @@ impl Printer<'_, '_> {
     }
 
     fn print_composite(&mut self, state: &State, ty: &CompositeType, ty_idx: u32) -> Result<u32> {
-        let r = match &ty {
-            CompositeType::Func(ty) => {
+        if ty.shared {
+            self.start_group("shared")?;
+        }
+        let r = match &ty.inner {
+            CompositeInnerType::Func(ty) => {
                 self.start_group("func")?;
                 let r = self.print_func_type(state, ty, None)?;
                 self.end_group()?; // `func`
                 r
             }
-            CompositeType::Array(ty) => {
+            CompositeInnerType::Array(ty) => {
                 self.start_group("array")?;
                 let r = self.print_array_type(state, ty)?;
                 self.end_group()?; // `array`
                 r
             }
-            CompositeType::Struct(ty) => {
+            CompositeInnerType::Struct(ty) => {
                 self.start_group("struct")?;
                 let r = self.print_struct_type(state, ty, ty_idx)?;
                 self.end_group()?; // `struct`
                 r
             }
         };
+        if ty.shared {
+            self.end_group()?; // `shared`
+        }
         Ok(r)
     }
 
@@ -881,7 +891,11 @@ impl Printer<'_, '_> {
 
         match state.core.types.get(idx as usize) {
             Some(Some(SubType {
-                composite_type: CompositeType::Func(ty),
+                composite_type:
+                    CompositeType {
+                        inner: CompositeInnerType::Func(ty),
+                        shared: false,
+                    },
                 ..
             })) => self.print_func_type(state, ty, names_for).map(Some),
             Some(Some(_)) | Some(None) | None => Ok(None),
diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs
index c2f3e0e2..c57d6333 100644
--- a/crates/wast/src/component/binary.rs
+++ b/crates/wast/src/component/binary.rs
@@ -57,14 +57,21 @@ fn encode_fields(
 
 fn encode_core_type(encoder: CoreTypeEncoder, ty: &CoreTypeDef) {
     match ty {
-        CoreTypeDef::Def(core::TypeDef::Func(f)) => {
-            encoder.function(
-                f.params.iter().map(|(_, _, ty)| (*ty).into()),
-                f.results.iter().copied().map(Into::into),
-            );
-        }
-        CoreTypeDef::Def(core::TypeDef::Struct(_)) | CoreTypeDef::Def(core::TypeDef::Array(_)) => {
-            todo!("encoding of GC proposal types not yet implemented")
+        CoreTypeDef::Def(def) => {
+            if def.shared {
+                todo!("encoding of shared types not yet implemented")
+            }
+            match &def.kind {
+                core::InnerTypeKind::Func(f) => {
+                    encoder.function(
+                        f.params.iter().map(|(_, _, ty)| (*ty).into()),
+                        f.results.iter().copied().map(Into::into),
+                    );
+                }
+                core::InnerTypeKind::Struct(_) | core::InnerTypeKind::Array(_) => {
+                    todo!("encoding of GC proposal types not yet implemented")
+                }
+            }
         }
         CoreTypeDef::Module(t) => {
             encoder.module(&t.into());
@@ -876,12 +883,12 @@ impl From<&ModuleType<'_>> for wasm_encoder::ModuleType {
 
         for decl in &ty.decls {
             match decl {
-                ModuleTypeDecl::Type(t) => match &t.def {
-                    core::TypeDef::Func(f) => encoded.ty().function(
+                ModuleTypeDecl::Type(t) => match &t.def.kind {
+                    core::InnerTypeKind::Func(f) => encoded.ty().function(
                         f.params.iter().map(|(_, _, ty)| (*ty).into()),
                         f.results.iter().copied().map(Into::into),
                     ),
-                    core::TypeDef::Struct(_) | core::TypeDef::Array(_) => {
+                    core::InnerTypeKind::Struct(_) | core::InnerTypeKind::Array(_) => {
                         todo!("encoding of GC proposal types not yet implemented")
                     }
                 },
diff --git a/crates/wast/src/component/expand.rs b/crates/wast/src/component/expand.rs
index 10523eac..0e41f331 100644
--- a/crates/wast/src/component/expand.rs
+++ b/crates/wast/src/component/expand.rs
@@ -428,13 +428,13 @@ impl<'a> Expander<'a> {
         let mut i = 0;
         while i < ty.decls.len() {
             match &mut ty.decls[i] {
-                ModuleTypeDecl::Type(ty) => match &ty.def {
-                    core::TypeDef::Func(f) => {
+                ModuleTypeDecl::Type(ty) => match &ty.def.kind {
+                    core::InnerTypeKind::Func(f) => {
                         let id = gensym::fill(ty.span, &mut ty.id);
                         func_type_to_idx.insert(f.key(), Index::Id(id));
                     }
-                    core::TypeDef::Struct(_) => {}
-                    core::TypeDef::Array(_) => {}
+                    core::InnerTypeKind::Struct(_) => {}
+                    core::InnerTypeKind::Array(_) => {}
                 },
                 ModuleTypeDecl::Alias(_) => {}
                 ModuleTypeDecl::Import(ty) => {
diff --git a/crates/wast/src/core/binary.rs b/crates/wast/src/core/binary.rs
index 8ce1b406..7beaa82e 100644
--- a/crates/wast/src/core/binary.rs
+++ b/crates/wast/src/core/binary.rs
@@ -361,16 +361,19 @@ impl Encode for Type<'_> {
             }
             (None, _) => {} // No supertype, sub wasn't used
         }
-        match &self.def {
-            TypeDef::Func(func) => {
+        if self.def.shared {
+            e.push(0x65);
+        }
+        match &self.def.kind {
+            InnerTypeKind::Func(func) => {
                 e.push(0x60);
                 func.encode(e)
             }
-            TypeDef::Struct(r#struct) => {
+            InnerTypeKind::Struct(r#struct) => {
                 e.push(0x5f);
                 r#struct.encode(e)
             }
-            TypeDef::Array(array) => {
+            InnerTypeKind::Array(array) => {
                 e.push(0x5e);
                 array.encode(e)
             }
@@ -1161,9 +1164,9 @@ fn find_names<'a>(
         // Handle struct fields separately from above
         if let ModuleField::Type(ty) = field {
             let mut field_names = vec![];
-            match &ty.def {
-                TypeDef::Func(_) | TypeDef::Array(_) => {}
-                TypeDef::Struct(ty_struct) => {
+            match &ty.def.kind {
+                InnerTypeKind::Func(_) | InnerTypeKind::Array(_) => {}
+                InnerTypeKind::Struct(ty_struct) => {
                     for (idx, field) in ty_struct.fields.iter().enumerate() {
                         if let Some(name) = get_name(&field.id, &None) {
                             field_names.push((idx as u32, name))
diff --git a/crates/wast/src/core/binary/dwarf.rs b/crates/wast/src/core/binary/dwarf.rs
index ae9c4306..b6aa086e 100644
--- a/crates/wast/src/core/binary/dwarf.rs
+++ b/crates/wast/src/core/binary/dwarf.rs
@@ -11,7 +11,7 @@
 //! easy/fun to play around with.
 
 use crate::core::binary::{EncodeOptions, Encoder, GenerateDwarf, Names, RecOrType};
-use crate::core::{Local, TypeDef, ValType};
+use crate::core::{InnerTypeKind, Local, ValType};
 use crate::token::Span;
 use gimli::write::{
     self, Address, AttributeValue, DwarfUnit, Expression, FileId, LineProgram, LineString,
@@ -227,8 +227,8 @@ impl<'a> Dwarf<'a> {
                 RecOrType::Rec(r) => &r.types,
             })
             .nth(ty as usize);
-        let ty = match ty.map(|t| &t.def) {
-            Some(TypeDef::Func(ty)) => ty,
+        let ty = match ty.map(|t| &t.def.kind) {
+            Some(InnerTypeKind::Func(ty)) => ty,
             _ => return,
         };
 
diff --git a/crates/wast/src/core/resolve/names.rs b/crates/wast/src/core/resolve/names.rs
index b73195cb..d2b7eb1d 100644
--- a/crates/wast/src/core/resolve/names.rs
+++ b/crates/wast/src/core/resolve/names.rs
@@ -49,12 +49,12 @@ impl<'a> Resolver<'a> {
     fn register_type(&mut self, ty: &Type<'a>) -> Result<(), Error> {
         let type_index = self.types.register(ty.id, "type")?;
 
-        match &ty.def {
+        match &ty.def.kind {
             // For GC structure types we need to be sure to populate the
             // field namespace here as well.
             //
             // The field namespace is relative to the struct fields are defined in
-            TypeDef::Struct(r#struct) => {
+            InnerTypeKind::Struct(r#struct) => {
                 for (i, field) in r#struct.fields.iter().enumerate() {
                     if let Some(id) = field.id {
                         self.fields
@@ -65,14 +65,14 @@ impl<'a> Resolver<'a> {
                 }
             }
 
-            TypeDef::Array(_) | TypeDef::Func(_) => {}
+            InnerTypeKind::Array(_) | InnerTypeKind::Func(_) => {}
         }
 
         // Record function signatures as we see them to so we can
         // generate errors for mismatches in references such as
         // `call_indirect`.
-        match &ty.def {
-            TypeDef::Func(f) => {
+        match &ty.def.kind {
+            InnerTypeKind::Func(f) => {
                 let params = f.params.iter().map(|p| p.2).collect();
                 let results = f.results.clone();
                 self.type_info.push(TypeInfo::Func { params, results });
@@ -120,14 +120,14 @@ impl<'a> Resolver<'a> {
     }
 
     fn resolve_type(&self, ty: &mut Type<'a>) -> Result<(), Error> {
-        match &mut ty.def {
-            TypeDef::Func(func) => func.resolve(self)?,
-            TypeDef::Struct(struct_) => {
+        match &mut ty.def.kind {
+            InnerTypeKind::Func(func) => func.resolve(self)?,
+            InnerTypeKind::Struct(struct_) => {
                 for field in &mut struct_.fields {
                     self.resolve_storagetype(&mut field.ty)?;
                 }
             }
-            TypeDef::Array(array) => self.resolve_storagetype(&mut array.ty)?,
+            InnerTypeKind::Array(array) => self.resolve_storagetype(&mut array.ty)?,
         }
         if let Some(parent) = &mut ty.parent {
             self.resolve(parent, Ns::Type)?;
diff --git a/crates/wast/src/core/resolve/types.rs b/crates/wast/src/core/resolve/types.rs
index 84b0e516..9172e502 100644
--- a/crates/wast/src/core/resolve/types.rs
+++ b/crates/wast/src/core/resolve/types.rs
@@ -51,11 +51,11 @@ impl<'a> Expander<'a> {
         match item {
             ModuleField::Type(ty) => {
                 let id = gensym::fill(ty.span, &mut ty.id);
-                match &mut ty.def {
-                    TypeDef::Func(f) => {
+                match &mut ty.def.kind {
+                    InnerTypeKind::Func(f) => {
                         f.key().insert(self, Index::Id(id));
                     }
-                    TypeDef::Array(_) | TypeDef::Struct(_) => {}
+                    InnerTypeKind::Array(_) | InnerTypeKind::Struct(_) => {}
                 }
             }
             _ => {}
@@ -255,10 +255,13 @@ impl<'a> TypeKey<'a> for FuncKey<'a> {
     }
 
     fn to_def(&self, _span: Span) -> TypeDef<'a> {
-        TypeDef::Func(FunctionType {
-            params: self.0.iter().map(|t| (None, None, *t)).collect(),
-            results: self.1.clone(),
-        })
+        TypeDef {
+            kind: InnerTypeKind::Func(FunctionType {
+                params: self.0.iter().map(|t| (None, None, *t)).collect(),
+                results: self.1.clone(),
+            }),
+            shared: false, // TODO: handle shared
+        }
     }
 
     fn insert(&self, cx: &mut Expander<'a>, idx: Index<'a>) {
diff --git a/crates/wast/src/core/types.rs b/crates/wast/src/core/types.rs
index b149f33d..48f128c0 100644
--- a/crates/wast/src/core/types.rs
+++ b/crates/wast/src/core/types.rs
@@ -864,9 +864,9 @@ impl<'a> Parse<'a> for ExportType<'a> {
     }
 }
 
-/// A definition of a type.
+/// The inner kind of a type definition.
 #[derive(Debug)]
-pub enum TypeDef<'a> {
+pub enum InnerTypeKind<'a> {
     /// A function type definition.
     Func(FunctionType<'a>),
     /// A struct type definition.
@@ -875,24 +875,52 @@ pub enum TypeDef<'a> {
     Array(ArrayType<'a>),
 }
 
-impl<'a> Parse<'a> for TypeDef<'a> {
+impl<'a> Parse<'a> for InnerTypeKind<'a> {
     fn parse(parser: Parser<'a>) -> Result<Self> {
         let mut l = parser.lookahead1();
         if l.peek::<kw::func>()? {
             parser.parse::<kw::func>()?;
-            Ok(TypeDef::Func(parser.parse()?))
+            Ok(InnerTypeKind::Func(parser.parse()?))
         } else if l.peek::<kw::r#struct>()? {
             parser.parse::<kw::r#struct>()?;
-            Ok(TypeDef::Struct(parser.parse()?))
+            Ok(InnerTypeKind::Struct(parser.parse()?))
         } else if l.peek::<kw::array>()? {
             parser.parse::<kw::array>()?;
-            Ok(TypeDef::Array(parser.parse()?))
+            Ok(InnerTypeKind::Array(parser.parse()?))
         } else {
             Err(l.error())
         }
     }
 }
 
+/// A definition of a type.
+#[derive(Debug)]
+pub struct TypeDef<'a> {
+    /// The inner definition.
+    pub kind: InnerTypeKind<'a>,
+    /// Whether the type is shared or not.
+    pub shared: bool,
+}
+
+impl<'a> Parse<'a> for TypeDef<'a> {
+    fn parse(parser: Parser<'a>) -> Result<Self> {
+        let mut l = parser.lookahead1();
+        if l.peek::<kw::shared>()? {
+            parser.parse::<kw::shared>()?;
+            parser.parens(|parser| {
+                let kind = parser.parse()?;
+                Ok(TypeDef { shared: true, kind })
+            })
+        } else {
+            let kind = parser.parse()?;
+            Ok(TypeDef {
+                shared: false,
+                kind,
+            })
+        }
+    }
+}
+
 /// A type declaration in a module
 #[derive(Debug)]
 pub struct Type<'a> {


Test patch:
diff --git a/crates/wasm-smith/tests/exports.rs b/crates/wasm-smith/tests/exports.rs
index 6d3e4451..7c48cd3d 100644
--- a/crates/wasm-smith/tests/exports.rs
+++ b/crates/wasm-smith/tests/exports.rs
@@ -107,7 +107,11 @@ fn get_func_and_global_exports(features: WasmFeatures, module: &[u8]) -> Vec<(St
                         let sub_type = types.get(core_id).expect("Failed to lookup core id");
                         assert!(sub_type.is_final);
                         assert!(sub_type.supertype_idx.is_none());
-                        let CompositeType::Func(func_type) = &sub_type.composite_type else {
+                        let CompositeType {
+                            inner: wasmparser::CompositeInnerType::Func(func_type),
+                            ..
+                        } = &sub_type.composite_type
+                        else {
                             panic!("Expected Func CompositeType, but found {:?}", sub_type);
                         };
                         exports
diff --git a/tests/cli/dump-branch-hints.wat.stdout b/tests/cli/dump-branch-hints.wat.stdout
index 7fe75765..9836b369 100644
--- a/tests/cli/dump-branch-hints.wat.stdout
+++ b/tests/cli/dump-branch-hints.wat.stdout
@@ -3,7 +3,7 @@
   0x8 | 01 04       | type section
   0xa | 01          | 1 count
 --- rec group 0 (implicit) ---
-  0xb | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+  0xb | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
   0xe | 03 02       | func section
  0x10 | 01          | 1 count
  0x11 | 00          | [func 0] type 0
diff --git a/tests/cli/dump-llvm-object.wat.stdout b/tests/cli/dump-llvm-object.wat.stdout
index 6d48409d..a421d723 100644
--- a/tests/cli/dump-llvm-object.wat.stdout
+++ b/tests/cli/dump-llvm-object.wat.stdout
@@ -3,21 +3,21 @@
    0x8 | 01 22       | type section
    0xa | 06          | 6 count
 --- rec group 0 (implicit) ---
-   0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [] }) }
+   0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false } }
 --- rec group 1 (implicit) ---
-   0xf | 60 04 7f 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32, I32, I32], results: [I32] }) }
+   0xf | 60 04 7f 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32, I32], results: [I32] }), shared: false } }
        | 7f 7f 01 7f
 --- rec group 2 (implicit) ---
-  0x17 | 60 05 7f 7f | [type 2] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32, I32, I32, I32], results: [I32] }) }
+  0x17 | 60 05 7f 7f | [type 2] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32, I32, I32], results: [I32] }), shared: false } }
        | 7f 7f 7f 01
        | 7f         
 --- rec group 3 (implicit) ---
-  0x20 | 60 01 7f 01 | [type 3] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [I32] }) }
+  0x20 | 60 01 7f 01 | [type 3] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [I32] }), shared: false } }
        | 7f         
 --- rec group 4 (implicit) ---
-  0x25 | 60 00 01 7f | [type 4] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [I32] }) }
+  0x25 | 60 00 01 7f | [type 4] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [I32] }), shared: false } }
 --- rec group 5 (implicit) ---
-  0x29 | 60 00 00    | [type 5] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+  0x29 | 60 00 00    | [type 5] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
   0x2c | 02 8b 01    | import section
   0x2f | 04          | 4 count
   0x30 | 03 65 6e 76 | import [memory 0] Import { module: "env", name: "__linear_memory", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None, page_size_log2: None }) }
diff --git a/tests/cli/dump/alias.wat.stdout b/tests/cli/dump/alias.wat.stdout
index 408c8678..aa444eaf 100644
--- a/tests/cli/dump/alias.wat.stdout
+++ b/tests/cli/dump/alias.wat.stdout
@@ -31,7 +31,7 @@
    0x57 | 01 04       | type section
    0x59 | 01          | 1 count
 --- rec group 0 (implicit) ---
-   0x5a | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+   0x5a | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
    0x5d | 03 02       | func section
    0x5f | 01          | 1 count
    0x60 | 00          | [func 0] type 0
diff --git a/tests/cli/dump/alias2.wat.stdout b/tests/cli/dump/alias2.wat.stdout
index 6601ad88..2710d46e 100644
--- a/tests/cli/dump/alias2.wat.stdout
+++ b/tests/cli/dump/alias2.wat.stdout
@@ -127,7 +127,7 @@
    0x158 | 01 04       | type section
    0x15a | 01          | 1 count
 --- rec group 0 (implicit) ---
-   0x15b | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+   0x15b | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
    0x15e | 03 02       | func section
    0x160 | 01          | 1 count
    0x161 | 00          | [func 0] type 0
@@ -164,7 +164,7 @@
    0x1a2 | 01 04       | type section
    0x1a4 | 01          | 1 count
 --- rec group 0 (implicit) ---
-   0x1a5 | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+   0x1a5 | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
    0x1a8 | 02 19       | import section
    0x1aa | 04          | 4 count
    0x1ab | 00 01 31 00 | import [func 0] Import { module: "", name: "1", ty: Func(0) }
diff --git a/tests/cli/dump/blockty.wat.stdout b/tests/cli/dump/blockty.wat.stdout
index ed70b520..a923deea 100644
--- a/tests/cli/dump/blockty.wat.stdout
+++ b/tests/cli/dump/blockty.wat.stdout
@@ -3,16 +3,16 @@
   0x8 | 01 17       | type section
   0xa | 05          | 5 count
 --- rec group 0 (implicit) ---
-  0xb | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+  0xb | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
 --- rec group 1 (implicit) ---
-  0xe | 60 00 01 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [I32] }) }
+  0xe | 60 00 01 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [I32] }), shared: false } }
 --- rec group 2 (implicit) ---
- 0x12 | 60 01 7f 00 | [type 2] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [] }) }
+ 0x12 | 60 01 7f 00 | [type 2] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false } }
 --- rec group 3 (implicit) ---
- 0x16 | 60 01 7f 01 | [type 3] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [I32] }) }
+ 0x16 | 60 01 7f 01 | [type 3] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [I32] }), shared: false } }
       | 7f         
 --- rec group 4 (implicit) ---
- 0x1b | 60 01 7f 02 | [type 4] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [I32, I32] }) }
+ 0x1b | 60 01 7f 02 | [type 4] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [I32, I32] }), shared: false } }
       | 7f 7f      
  0x21 | 03 02       | func section
  0x23 | 01          | 1 count
diff --git a/tests/cli/dump/bundled.wat.stdout b/tests/cli/dump/bundled.wat.stdout
index 4ea95d9e..4408c982 100644
--- a/tests/cli/dump/bundled.wat.stdout
+++ b/tests/cli/dump/bundled.wat.stdout
@@ -26,7 +26,7 @@
     0x54 | 01 09       | type section
     0x56 | 01          | 1 count
 --- rec group 0 (implicit) ---
-    0x57 | 60 04 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32, I32, I32], results: [I32] }) }
+    0x57 | 60 04 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32, I32], results: [I32] }), shared: false } }
          | 7f 7f 01 7f
     0x5f | 03 02       | func section
     0x61 | 01          | 1 count
@@ -63,10 +63,10 @@
     0xa0 | 01 09       | type section
     0xa2 | 02          | 2 count
 --- rec group 0 (implicit) ---
-    0xa3 | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32], results: [] }) }
+    0xa3 | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32], results: [] }), shared: false } }
          | 00         
 --- rec group 1 (implicit) ---
-    0xa8 | 60 00 00    | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+    0xa8 | 60 00 00    | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
     0xab | 02 12       | import section
     0xad | 01          | 1 count
     0xae | 09 77 61 73 | import [func 0] Import { module: "wasi-file", name: "read", ty: Func(0) }
@@ -107,10 +107,10 @@
    0x101 | 01 0c       | type section
    0x103 | 02          | 2 count
 --- rec group 0 (implicit) ---
-   0x104 | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32], results: [] }) }
+   0x104 | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32], results: [] }), shared: false } }
          | 00         
 --- rec group 1 (implicit) ---
-   0x109 | 60 03 7f 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32, I32], results: [] }) }
+   0x109 | 60 03 7f 7f | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32, I32], results: [] }), shared: false } }
          | 7f 00      
    0x10f | 02 12       | import section
    0x111 | 01          | 1 count
diff --git a/tests/cli/dump/component-expand-bundle.wat.stdout b/tests/cli/dump/component-expand-bundle.wat.stdout
index aa82e1f0..d46b1317 100644
--- a/tests/cli/dump/component-expand-bundle.wat.stdout
+++ b/tests/cli/dump/component-expand-bundle.wat.stdout
@@ -6,7 +6,7 @@
    0x12 | 01 04       | type section
    0x14 | 01          | 1 count
 --- rec group 0 (implicit) ---
-   0x15 | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+   0x15 | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
    0x18 | 03 02       | func section
    0x1a | 01          | 1 count
    0x1b | 00          | [func 0] type 0
@@ -30,7 +30,7 @@
    0x3d | 01 04       | type section
    0x3f | 01          | 1 count
 --- rec group 0 (implicit) ---
-   0x40 | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+   0x40 | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
    0x43 | 02 06       | import section
    0x45 | 01          | 1 count
    0x46 | 00 01 61 00 | import [func 0] Import { module: "", name: "a", ty: Func(0) }
diff --git a/tests/cli/dump/import-modules.wat.stdout b/tests/cli/dump/import-modules.wat.stdout
index 3e647545..a128f8a5 100644
--- a/tests/cli/dump/import-modules.wat.stdout
+++ b/tests/cli/dump/import-modules.wat.stdout
@@ -2,7 +2,7 @@
       | 0d 00 01 00
   0x8 | 03 0d       | core type section
   0xa | 01          | 1 count
-  0xb | 50 02 01 60 | [core type 0] Module([Type(SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }), Import(Import { module: "", name: "f", ty: Func(0) })])
+  0xb | 50 02 01 60 | [core type 0] Module([Type(SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }), Import(Import { module: "", name: "f", ty: Func(0) })])
       | 00 00 00 00
       | 01 66 00 00
  0x17 | 0a 07       | component import section
@@ -15,7 +15,7 @@
    0x2a | 01 04       | type section
    0x2c | 01          | 1 count
 --- rec group 0 (implicit) ---
-   0x2d | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+   0x2d | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
    0x30 | 03 02       | func section
    0x32 | 01          | 1 count
    0x33 | 00          | [func 0] type 0
diff --git a/tests/cli/dump/module-types.wat.stdout b/tests/cli/dump/module-types.wat.stdout
index d9d16f4a..ddde806e 100644
--- a/tests/cli/dump/module-types.wat.stdout
+++ b/tests/cli/dump/module-types.wat.stdout
@@ -2,7 +2,7 @@
       | 0d 00 01 00
   0x8 | 03 23       | core type section
   0xa | 01          | 1 count
-  0xb | 50 05 01 60 | [core type 0] Module([Type(SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }), Import(Import { module: "", name: "f", ty: Func(0) }), Import(Import { module: "", name: "g", ty: Global(GlobalType { content_type: I32, mutable: false, shared: false }) }), Import(Import { module: "", name: "t", ty: Table(TableType { element_type: funcref, table64: false, initial: 1, maximum: None }) }), Import(Import { module: "", name: "m", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None, page_size_log2: None }) })])
+  0xb | 50 05 01 60 | [core type 0] Module([Type(SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }), Import(Import { module: "", name: "f", ty: Func(0) }), Import(Import { module: "", name: "g", ty: Global(GlobalType { content_type: I32, mutable: false, shared: false }) }), Import(Import { module: "", name: "t", ty: Table(TableType { element_type: funcref, table64: false, initial: 1, maximum: None }) }), Import(Import { module: "", name: "m", ty: Memory(MemoryType { memory64: false, shared: false, initial: 1, maximum: None, page_size_log2: None }) })])
       | 00 00 00 00
       | 01 66 00 00
       | 00 00 01 67
diff --git a/tests/cli/dump/names.wat.stdout b/tests/cli/dump/names.wat.stdout
index 3585c91e..41ee06cc 100644
--- a/tests/cli/dump/names.wat.stdout
+++ b/tests/cli/dump/names.wat.stdout
@@ -3,7 +3,7 @@
   0x8 | 01 05       | type section
   0xa | 01          | 1 count
 --- rec group 0 (implicit) ---
-  0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [] }) }
+  0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false } }
   0xf | 03 02       | func section
  0x11 | 01          | 1 count
  0x12 | 00          | [func 0] type 0
diff --git a/tests/cli/dump/rec-group.wat.stdout b/tests/cli/dump/rec-group.wat.stdout
index 7d4e5fa9..07317e38 100644
--- a/tests/cli/dump/rec-group.wat.stdout
+++ b/tests/cli/dump/rec-group.wat.stdout
@@ -4,25 +4,25 @@
   0xa | 04          | 4 count
 --- rec group 0 (explicit) ---
   0xb | 4e 01       | 
-  0xd | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32, I32], results: [F64] }) }
+  0xd | 60 02 7f 7f | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32, I32], results: [F64] }), shared: false } }
       | 01 7c      
 --- rec group 1 (explicit) ---
  0x13 | 4e 03       | 
- 0x15 | 50 00 5f 01 | [type 1] SubType { is_final: false, supertype_idx: None, composite_type: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: false }] }) }
+ 0x15 | 50 00 5f 01 | [type 1] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: false }] }), shared: false } }
       | 7f 00      
- 0x1b | 50 00 5f 01 | [type 2] SubType { is_final: false, supertype_idx: None, composite_type: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: true }] }) }
+ 0x1b | 50 00 5f 01 | [type 2] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: true }] }), shared: false } }
       | 7f 01      
- 0x21 | 50 00 5f 08 | [type 3] SubType { is_final: false, supertype_idx: None, composite_type: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: true }, FieldType { element_type: Val(I64), mutable: true }, FieldType { element_type: Val(F32), mutable: true }, FieldType { element_type: Val(F64), mutable: true }, FieldType { element_type: Val(V128), mutable: true }, FieldType { element_type: Val(Ref(funcref)), mutable: true }, FieldType { element_type: Val(Ref(externref)), mutable: true }, FieldType { element_type: Val(Ref((ref null (module 2)))), mutable: true }] }) }
+ 0x21 | 50 00 5f 08 | [type 3] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Struct(StructType { fields: [FieldType { element_type: Val(I32), mutable: true }, FieldType { element_type: Val(I64), mutable: true }, FieldType { element_type: Val(F32), mutable: true }, FieldType { element_type: Val(F64), mutable: true }, FieldType { element_type: Val(V128), mutable: true }, FieldType { element_type: Val(Ref(funcref)), mutable: true }, FieldType { element_type: Val(Ref(externref)), mutable: true }, FieldType { element_type: Val(Ref((ref null (module 2)))), mutable: true }] }), shared: false } }
       | 7f 01 7e 01
       | 7d 01 7c 01
       | 7b 01 70 01
       | 6f 01 63 02
       | 01         
 --- rec group 2 (implicit) ---
- 0x36 | 50 00 5e 7f | [type 4] SubType { is_final: false, supertype_idx: None, composite_type: Array(ArrayType(FieldType { element_type: Val(I32), mutable: false })) }
+ 0x36 | 50 00 5e 7f | [type 4] SubType { is_final: false, supertype_idx: None, composite_type: CompositeType { inner: Array(ArrayType(FieldType { element_type: Val(I32), mutable: false })), shared: false } }
       | 00         
 --- rec group 3 (implicit) ---
- 0x3b | 50 01 04 5e | [type 5] SubType { is_final: false, supertype_idx: Some(CoreTypeIndex { kind: "module", index: 4 }), composite_type: Array(ArrayType(FieldType { element_type: Val(I32), mutable: false })) }
+ 0x3b | 50 01 04 5e | [type 5] SubType { is_final: false, supertype_idx: Some(CoreTypeIndex { kind: "module", index: 4 }), composite_type: CompositeType { inner: Array(ArrayType(FieldType { element_type: Val(I32), mutable: false })), shared: false } }
       | 7f 00      
  0x41 | 00 0e       | custom section
  0x43 | 04 6e 61 6d | name: "name"
diff --git a/tests/cli/dump/select.wat.stdout b/tests/cli/dump/select.wat.stdout
index 48f950ae..be65160b 100644
--- a/tests/cli/dump/select.wat.stdout
+++ b/tests/cli/dump/select.wat.stdout
@@ -3,7 +3,7 @@
   0x8 | 01 04       | type section
   0xa | 01          | 1 count
 --- rec group 0 (implicit) ---
-  0xb | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+  0xb | 60 00 00    | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
   0xe | 03 02       | func section
  0x10 | 01          | 1 count
  0x11 | 00          | [func 0] type 0
diff --git a/tests/cli/dump/simple.wat.stdout b/tests/cli/dump/simple.wat.stdout
index 27fc8a4d..59e8902f 100644
--- a/tests/cli/dump/simple.wat.stdout
+++ b/tests/cli/dump/simple.wat.stdout
@@ -3,9 +3,9 @@
   0x8 | 01 08       | type section
   0xa | 02          | 2 count
 --- rec group 0 (implicit) ---
-  0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [I32], results: [] }) }
+  0xb | 60 01 7f 00 | [type 0] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [I32], results: [] }), shared: false } }
 --- rec group 1 (implicit) ---
-  0xf | 60 00 00    | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: Func(FuncType { params: [], results: [] }) }
+  0xf | 60 00 00    | [type 1] SubType { is_final: true, supertype_idx: None, composite_type: CompositeType { inner: Func(FuncType { params: [], results: [] }), shared: false } }
  0x12 | 02 07       | import section
  0x14 | 01          | 1 count
  0x15 | 01 6d 01 6e | import [func 0] Import { module: "m", name: "n", ty: Func(0) }
diff --git a/tests/local/shared-everything-threads/array.wast b/tests/local/shared-everything-threads/array.wast
new file mode 100644
index 00000000..e3fb3adb
--- /dev/null
+++ b/tests/local/shared-everything-threads/array.wast
@@ -0,0 +1,115 @@
+;; Shared array declaration syntax
+(module
+  (type (shared (array i8)))
+  (type (sub final (shared (array i8))))
+  (rec
+    (type (sub final (shared (array i8))))
+  )
+
+  (global (ref 0) (array.new_default 1 (i32.const 1)))
+  (global (ref 1) (array.new_default 2 (i32.const 1)))
+  (global (ref 2) (array.new_default 0 (i32.const 1)))
+)
+
+;; Shared arrays are distinct from non-shared arrays
+(assert_invalid
+  (module
+    (type (shared (array i8)))
+    (type (array i8))
+
+    (global (ref 0) (array.new_default 1 (i32.const 1)))
+  )
+  "type mismatch"
+)
+
+(assert_invalid
+  (module
+    (type (shared (array i8)))
+    (type (array i8))
+
+    (global (ref 1) (array.new 0))
+  )
+  "type mismatch"
+)
+
+;; Shared arrays may not be subtypes of non-shared arrays
+(assert_invalid
+  (module
+    (type (sub (array i8)))
+    (type (sub 0 (shared (array i8))))
+  )
+  "sub type must match super type"
+)
+
+;; Non-shared arrays may not be subtypes of shared arrays
+(assert_invalid
+  (module
+    (type (sub (shared (array i8))))
+    (type (sub 0 (array i8)))
+  )
+  "sub type must match super type"
+)
+
+;; Shared arrays may not contain non-shared references
+(assert_invalid
+  (module
+    (type (shared (array anyref)))
+  )
+  "must contain shared type"
+)
+
+;; But they may contain shared references
+(module
+  (type (shared (array (ref null (shared any)))))
+)
+
+;; Non-shared arrays may contain shared references
+(module
+  (type (array (ref null (shared any))))
+)
+
+;; Array instructions work on shared arrays.
+(module
+  (type $i8 (shared (array (mut i8))))
+  (type $i32 (shared (array (mut i32))))
+  (type $unshared (array (mut i8)))
+
+  (data)
+  (elem arrayref)
+
+  (func (array.new $i8 (i32.const 0) (i32.const 0)) (drop))
+
+  (func (array.new_default $i8 (i32.const 0)) (drop))
+
+  (func (array.new_fixed $i8 0) (drop))
+
+  (func (param (ref null $i8))
+    (array.get_s $i8 (local.get 0) (i32.const 0)) (drop))
+
+  (func (param (ref null $i8))
+    (array.get_u $i8 (local.get 0) (i32.const 0)) (drop))
+
+  (func (param (ref null $i32))
+    (array.get $i32 (local.get 0) (i32.const 0)) (drop))
+
+  (func (param (ref null $i8))
+    (array.set $i8 (local.get 0) (i32.const 0) (i32.const 0)))
+
+  (func (param (ref null $i8) (ref null $i8))
+    (array.copy $i8 $i8 (local.get 0) (i32.const 0) (local.get 1) (i32.const 0) (i32.const 0)))
+
+  (func (param (ref null $i8) (ref null $unshared))
+    (array.copy $i8 $unshared (local.get 0) (i32.const 0) (local.get 1) (i32.const 0) (i32.const 0)))
+
+  (func (param (ref null $unshared) (ref null $i8))
+    (array.copy $unshared $i8 (local.get 0) (i32.const 0) (local.get 1) (i32.const 0) (i32.const 0)))
+
+  (func (param (ref null $i8))
+    (array.fill $i8 (local.get 0) (i32.const 0) (i32.const 0) (i32.const 0)))
+
+  (func (param (ref null $i8))
+    (array.init_data $i8 0 (local.get 0) (i32.const 0) (i32.const 0) (i32.const 0)))
+
+  (func (param (ref null $i8))
+    (array.init_data $i8 0 (local.get 0) (i32.const 0) (i32.const 0) (i32.const 0)))
+)
diff --git a/tests/local/shared-everything-threads/global-heap-types.wast b/tests/local/shared-everything-threads/global-heap-types.wast
index 4b887fc1..a94df317 100644
--- a/tests/local/shared-everything-threads/global-heap-types.wast
+++ b/tests/local/shared-everything-threads/global-heap-types.wast
@@ -1,14 +1,5 @@
 ;; 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).
@@ -309,3 +300,89 @@
   (module (global (ref (shared none))))
   "type mismatch")
 
+;; Concrete `func` references.
+(module
+  (type $t (shared (func)))
+
+  ;; Imported.
+  (global (import "spectest" "global_t") (shared (ref $t)))
+  (global (import "spectest" "global_mut_t") (shared mut (ref $t)))
+  (global (import "spectest" "global_null_t") (shared (ref null $t)))
+  (global (import "spectest" "global_mut_null_t") (shared mut (ref null $t)))
+
+  ;; Initialized.
+  (global (shared (ref null $t)) (ref.null $t))
+  (global (shared mut (ref null $t)) (ref.null $t))
+)
+
+(assert_invalid
+  (module
+    (type $t (func))
+    (global (shared (ref $t))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module
+    (type $t (shared (func)))
+    (global (ref $t)))
+  "type mismatch")
+(assert_invalid
+  (module (type $t (shared (func (param funcref)))))
+  "shared composite type must contain shared types")
+
+;; Concrete `array` references.
+(module
+  (type $t (shared (array i32)))
+
+  ;; Imported.
+  (global (import "spectest" "global_t") (shared (ref $t)))
+  (global (import "spectest" "global_mut_t") (shared mut (ref $t)))
+  (global (import "spectest" "global_null_t") (shared (ref null $t)))
+  (global (import "spectest" "global_mut_null_t") (shared mut (ref null $t)))
+
+  ;; Initialized.
+  (global (shared (ref null $t)) (ref.null $t))
+  (global (shared mut (ref null $t)) (ref.null $t))
+)
+
+(assert_invalid
+  (module
+    (type $t (array i32))
+    (global (shared (ref $t))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module
+    (type $t (shared (array i32)))
+    (global (ref $t)))
+  "type mismatch")
+(assert_invalid
+  (module (type $t (shared (array funcref))))
+  "shared composite type must contain shared types")
+
+;; Concrete `struct` references.
+(module
+  (type $t (shared (struct (field i32))))
+
+  ;; Imported.
+  (global (import "spectest" "global_t") (shared (ref $t)))
+  (global (import "spectest" "global_mut_t") (shared mut (ref $t)))
+  (global (import "spectest" "global_null_t") (shared (ref null $t)))
+  (global (import "spectest" "global_mut_null_t") (shared mut (ref null $t)))
+
+  ;; Initialized.
+  (global (shared (ref null $t)) (ref.null $t))
+  (global (shared mut (ref null $t)) (ref.null $t))
+)
+
+(assert_invalid
+  (module
+    (type $t (struct (field i32)))
+    (global (shared (ref $t))))
+  "shared globals must have a shared value type")
+(assert_invalid
+  (module
+    (type $t (shared (struct (field i32))))
+    (global (ref $t)))
+  "type mismatch")
+(assert_invalid
+  (module (type $t (shared (struct (field funcref)))))
+  "shared composite type must contain shared types")
diff --git a/tests/local/shared-everything-threads/struct.wast b/tests/local/shared-everything-threads/struct.wast
new file mode 100644
index 00000000..91b1cec2
--- /dev/null
+++ b/tests/local/shared-everything-threads/struct.wast
@@ -0,0 +1,92 @@
+;; Shared struct declaration syntax
+(module
+  (type (shared (struct)))
+  (type (sub final (shared (struct))))
+  (rec
+    (type (sub final (shared (struct))))
+  )
+
+  (global (ref 0) (struct.new 1))
+  (global (ref 1) (struct.new 2))
+  (global (ref 2) (struct.new 0))
+)
+
+;; Shared structs are distinct from non-shared structs
+(assert_invalid
+  (module
+    (type (shared (struct)))
+    (type (struct))
+
+    (global (ref 0) (struct.new 1))
+  )
+  "type mismatch"
+)
+
+(assert_invalid
+  (module
+    (type (shared (struct)))
+    (type (struct))
+
+    (global (ref 1) (struct.new 0))
+  )
+  "type mismatch"
+)
+
+;; Shared structs may not be subtypes of non-shared structs
+(assert_invalid
+  (module
+    (type (sub (struct)))
+    (type (sub 0 (shared (struct))))
+  )
+  "must match super type"
+)
+
+;; Non-shared structs may not be subtypes of shared structs
+(assert_invalid
+  (module
+    (type (sub (shared (struct))))
+    (type (sub 0 (struct)))
+  )
+  "must match super type"
+)
+
+;; Shared structs may not contain non-shared references
+(assert_invalid
+  (module
+    (type (shared (struct (field anyref))))
+  )
+  "must contain shared type"
+)
+
+;; But they may contain shared references
+(module
+  (type (shared (struct (field (ref null (shared any))))))
+)
+
+;; Non-shared structs may contain shared references
+(module
+  (type (struct (field (ref null (shared any)))))
+)
+
+;; Struct instructions work on shared structs.
+(module
+  (type $i8 (shared (struct (field (mut i8)))))
+  (type $i32 (shared (struct (field (mut i32)))))
+  (type $unshared (struct (field (mut i8))))
+
+  (func (struct.new $i8 (i32.const 0)) (drop))
+
+  (func (struct.new_default $i8) (drop))
+
+  (func (param (ref null $i8))
+    (struct.get_s $i8 0 (local.get 0)) (drop))
+
+  (func (param (ref null $i8))
+    (struct.get_u $i8 0 (local.get 0)) (drop))
+
+  (func (param (ref null $i32))
+    (struct.get $i32 0 (local.get 0)) (drop))
+
+  (func (param (ref null $i8))
+    (struct.set $i8 0 (local.get 0) (i32.const 0)))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast.json b/tests/snapshots/local/shared-everything-threads/array.wast.json
new file mode 100644
index 00000000..03bf81d7
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast.json
@@ -0,0 +1,60 @@
+{
+  "source_filename": "tests/local/shared-everything-threads/array.wast",
+  "commands": [
+    {
+      "type": "module",
+      "line": 2,
+      "filename": "array.0.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 16,
+      "filename": "array.1.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 26,
+      "filename": "array.2.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 37,
+      "filename": "array.3.wasm",
+      "text": "sub type must match super type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 46,
+      "filename": "array.4.wasm",
+      "text": "sub type must match super type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 55,
+      "filename": "array.5.wasm",
+      "text": "must contain shared type",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 62,
+      "filename": "array.6.wasm"
+    },
+    {
+      "type": "module",
+      "line": 67,
+      "filename": "array.7.wasm"
+    },
+    {
+      "type": "module",
+      "line": 72,
+      "filename": "array.8.wasm"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/0.print b/tests/snapshots/local/shared-everything-threads/array.wast/0.print
new file mode 100644
index 00000000..d875a17e
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/0.print
@@ -0,0 +1,10 @@
+(module
+  (type (;0;) (shared(array i8)))
+  (type (;1;) (shared(array i8)))
+  (rec
+    (type (;2;) (shared(array i8)))
+  )
+  (global (;0;) (ref 0) i32.const 1 array.new_default 1)
+  (global (;1;) (ref 1) i32.const 1 array.new_default 2)
+  (global (;2;) (ref 2) i32.const 1 array.new_default 0)
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/6.print b/tests/snapshots/local/shared-everything-threads/array.wast/6.print
new file mode 100644
index 00000000..7ea8fb8f
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/6.print
@@ -0,0 +1,3 @@
+(module
+  (type (;0;) (shared(array (ref null (shared any)))))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/7.print b/tests/snapshots/local/shared-everything-threads/array.wast/7.print
new file mode 100644
index 00000000..949220be
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/7.print
@@ -0,0 +1,3 @@
+(module
+  (type (;0;) (array (ref null (shared any))))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/array.wast/8.print b/tests/snapshots/local/shared-everything-threads/array.wast/8.print
new file mode 100644
index 00000000..03555883
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/array.wast/8.print
@@ -0,0 +1,97 @@
+(module
+  (type $i8 (;0;) (shared(array (mut i8))))
+  (type $i32 (;1;) (shared(array (mut i32))))
+  (type $unshared (;2;) (array (mut i8)))
+  (type (;3;) (func))
+  (type (;4;) (func (param (ref null $i8))))
+  (type (;5;) (func (param (ref null $i32))))
+  (type (;6;) (func (param (ref null $i8) (ref null $i8))))
+  (type (;7;) (func (param (ref null $i8) (ref null $unshared))))
+  (type (;8;) (func (param (ref null $unshared) (ref null $i8))))
+  (func (;0;) (type 3)
+    i32.const 0
+    i32.const 0
+    array.new $i8
+    drop
+  )
+  (func (;1;) (type 3)
+    i32.const 0
+    array.new_default $i8
+    drop
+  )
+  (func (;2;) (type 3)
+    array.new_fixed $i8 0
+    drop
+  )
+  (func (;3;) (type 4) (param (ref null $i8))
+    local.get 0
+    i32.const 0
+    array.get_s $i8
+    drop
+  )
+  (func (;4;) (type 4) (param (ref null $i8))
+    local.get 0
+    i32.const 0
+    array.get_u $i8
+    drop
+  )
+  (func (;5;) (type 5) (param (ref null $i32))
+    local.get 0
+    i32.const 0
+    array.get $i32
+    drop
+  )
+  (func (;6;) (type 4) (param (ref null $i8))
+    local.get 0
+    i32.const 0
+    i32.const 0
+    array.set $i8
+  )
+  (func (;7;) (type 6) (param (ref null $i8) (ref null $i8))
+    local.get 0
+    i32.const 0
+    local.get 1
+    i32.const 0
+    i32.const 0
+    array.copy $i8 $i8
+  )
+  (func (;8;) (type 7) (param (ref null $i8) (ref null $unshared))
+    local.get 0
+    i32.const 0
+    local.get 1
+    i32.const 0
+    i32.const 0
+    array.copy $i8 $unshared
+  )
+  (func (;9;) (type 8) (param (ref null $unshared) (ref null $i8))
+    local.get 0
+    i32.const 0
+    local.get 1
+    i32.const 0
+    i32.const 0
+    array.copy $unshared $i8
+  )
+  (func (;10;) (type 4) (param (ref null $i8))
+    local.get 0
+    i32.const 0
+    i32.const 0
+    i32.const 0
+    array.fill $i8
+  )
+  (func (;11;) (type 4) (param (ref null $i8))
+    local.get 0
+    i32.const 0
+    i32.const 0
+    i32.const 0
+    array.init_data $i8 0
+  )
+  (func (;12;) (type 4) (param (ref null $i8))
+    local.get 0
+    i32.const 0
+    i32.const 0
+    i32.const 0
+    array.init_data $i8 0
+  )
+  (elem (;0;) arrayref)
+  (data (;0;) "")
+)
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
index 526b9da7..e0a82de6 100644
--- a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast.json
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast.json
@@ -3,315 +3,393 @@
   "commands": [
     {
       "type": "module",
-      "line": 13,
+      "line": 4,
       "filename": "global-heap-types.0.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 28,
+      "line": 19,
       "filename": "global-heap-types.1.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 31,
+      "line": 22,
       "filename": "global-heap-types.2.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 34,
+      "line": 25,
       "filename": "global-heap-types.3.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 38,
+      "line": 29,
       "filename": "global-heap-types.4.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 53,
+      "line": 44,
       "filename": "global-heap-types.5.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 56,
+      "line": 47,
       "filename": "global-heap-types.6.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 59,
+      "line": 50,
       "filename": "global-heap-types.7.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 63,
+      "line": 54,
       "filename": "global-heap-types.8.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 78,
+      "line": 69,
       "filename": "global-heap-types.9.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 81,
+      "line": 72,
       "filename": "global-heap-types.10.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 84,
+      "line": 75,
       "filename": "global-heap-types.11.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 88,
+      "line": 79,
       "filename": "global-heap-types.12.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 103,
+      "line": 94,
       "filename": "global-heap-types.13.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 106,
+      "line": 97,
       "filename": "global-heap-types.14.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 109,
+      "line": 100,
       "filename": "global-heap-types.15.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 113,
+      "line": 104,
       "filename": "global-heap-types.16.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 128,
+      "line": 119,
       "filename": "global-heap-types.17.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 131,
+      "line": 122,
       "filename": "global-heap-types.18.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 134,
+      "line": 125,
       "filename": "global-heap-types.19.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 138,
+      "line": 129,
       "filename": "global-heap-types.20.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 153,
+      "line": 144,
       "filename": "global-heap-types.21.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 156,
+      "line": 147,
       "filename": "global-heap-types.22.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 159,
+      "line": 150,
       "filename": "global-heap-types.23.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 163,
+      "line": 154,
       "filename": "global-heap-types.24.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 178,
+      "line": 169,
       "filename": "global-heap-types.25.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 181,
+      "line": 172,
       "filename": "global-heap-types.26.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 184,
+      "line": 175,
       "filename": "global-heap-types.27.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 188,
+      "line": 179,
       "filename": "global-heap-types.28.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 203,
+      "line": 194,
       "filename": "global-heap-types.29.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 206,
+      "line": 197,
       "filename": "global-heap-types.30.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 209,
+      "line": 200,
       "filename": "global-heap-types.31.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 213,
+      "line": 204,
       "filename": "global-heap-types.32.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 228,
+      "line": 219,
       "filename": "global-heap-types.33.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 231,
+      "line": 222,
       "filename": "global-heap-types.34.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 234,
+      "line": 225,
       "filename": "global-heap-types.35.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 238,
+      "line": 229,
       "filename": "global-heap-types.36.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 253,
+      "line": 244,
       "filename": "global-heap-types.37.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 256,
+      "line": 247,
       "filename": "global-heap-types.38.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 259,
+      "line": 250,
       "filename": "global-heap-types.39.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 263,
+      "line": 254,
       "filename": "global-heap-types.40.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 278,
+      "line": 269,
       "filename": "global-heap-types.41.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 281,
+      "line": 272,
       "filename": "global-heap-types.42.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 284,
+      "line": 275,
       "filename": "global-heap-types.43.wasm",
       "text": "type mismatch",
       "module_type": "binary"
     },
     {
       "type": "module",
-      "line": 288,
+      "line": 279,
       "filename": "global-heap-types.44.wasm"
     },
     {
       "type": "assert_invalid",
-      "line": 303,
+      "line": 294,
       "filename": "global-heap-types.45.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 306,
+      "line": 297,
       "filename": "global-heap-types.46.wasm",
       "text": "shared globals must have a shared value type",
       "module_type": "binary"
     },
     {
       "type": "assert_invalid",
-      "line": 309,
+      "line": 300,
       "filename": "global-heap-types.47.wasm",
       "text": "type mismatch",
       "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 304,
+      "filename": "global-heap-types.48.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 319,
+      "filename": "global-heap-types.49.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 324,
+      "filename": "global-heap-types.50.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 329,
+      "filename": "global-heap-types.51.wasm",
+      "text": "shared composite type must contain shared types",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 333,
+      "filename": "global-heap-types.52.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 348,
+      "filename": "global-heap-types.53.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 353,
+      "filename": "global-heap-types.54.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 358,
+      "filename": "global-heap-types.55.wasm",
+      "text": "shared composite type must contain shared types",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 362,
+      "filename": "global-heap-types.56.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 377,
+      "filename": "global-heap-types.57.wasm",
+      "text": "shared globals must have a shared value type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 382,
+      "filename": "global-heap-types.58.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 387,
+      "filename": "global-heap-types.59.wasm",
+      "text": "shared composite type must contain shared types",
+      "module_type": "binary"
     }
   ]
 }
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/48.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/48.print
new file mode 100644
index 00000000..23f2967b
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/48.print
@@ -0,0 +1,9 @@
+(module
+  (type $t (;0;) (shared(func)))
+  (import "spectest" "global_t" (global (;0;) (shared (ref $t))))
+  (import "spectest" "global_mut_t" (global (;1;) (shared mut (ref $t))))
+  (import "spectest" "global_null_t" (global (;2;) (shared (ref null $t))))
+  (import "spectest" "global_mut_null_t" (global (;3;) (shared mut (ref null $t))))
+  (global (;4;) (shared (ref null $t)) ref.null $t)
+  (global (;5;) (shared mut (ref null $t)) ref.null $t)
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/52.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/52.print
new file mode 100644
index 00000000..82a7f609
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/52.print
@@ -0,0 +1,9 @@
+(module
+  (type $t (;0;) (shared(array i32)))
+  (import "spectest" "global_t" (global (;0;) (shared (ref $t))))
+  (import "spectest" "global_mut_t" (global (;1;) (shared mut (ref $t))))
+  (import "spectest" "global_null_t" (global (;2;) (shared (ref null $t))))
+  (import "spectest" "global_mut_null_t" (global (;3;) (shared mut (ref null $t))))
+  (global (;4;) (shared (ref null $t)) ref.null $t)
+  (global (;5;) (shared mut (ref null $t)) ref.null $t)
+)
diff --git a/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/56.print b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/56.print
new file mode 100644
index 00000000..ddf50f82
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/global-heap-types.wast/56.print
@@ -0,0 +1,9 @@
+(module
+  (type $t (;0;) (shared(struct (field i32))))
+  (import "spectest" "global_t" (global (;0;) (shared (ref $t))))
+  (import "spectest" "global_mut_t" (global (;1;) (shared mut (ref $t))))
+  (import "spectest" "global_null_t" (global (;2;) (shared (ref null $t))))
+  (import "spectest" "global_mut_null_t" (global (;3;) (shared mut (ref null $t))))
+  (global (;4;) (shared (ref null $t)) ref.null $t)
+  (global (;5;) (shared mut (ref null $t)) ref.null $t)
+)
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast.json b/tests/snapshots/local/shared-everything-threads/struct.wast.json
new file mode 100644
index 00000000..97900a0e
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/struct.wast.json
@@ -0,0 +1,60 @@
+{
+  "source_filename": "tests/local/shared-everything-threads/struct.wast",
+  "commands": [
+    {
+      "type": "module",
+      "line": 2,
+      "filename": "struct.0.wasm"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 16,
+      "filename": "struct.1.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 26,
+      "filename": "struct.2.wasm",
+      "text": "type mismatch",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 37,
+      "filename": "struct.3.wasm",
+      "text": "must match super type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 46,
+      "filename": "struct.4.wasm",
+      "text": "must match super type",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 55,
+      "filename": "struct.5.wasm",
+      "text": "must contain shared type",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 62,
+      "filename": "struct.6.wasm"
+    },
+    {
+      "type": "module",
+      "line": 67,
+      "filename": "struct.7.wasm"
+    },
+    {
+      "type": "module",
+      "line": 72,
+      "filename": "struct.8.wasm"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast/0.print b/tests/snapshots/local/shared-everything-threads/struct.wast/0.print
new file mode 100644
index 00000000..d0c306e0
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/struct.wast/0.print
@@ -0,0 +1,10 @@
+(module
+  (type (;0;) (shared(struct)))
+  (type (;1;) (shared(struct)))
+  (rec
+    (type (;2;) (shared(struct)))
+  )
+  (global (;0;) (ref 0) struct.new 1)
+  (global (;1;) (ref 1) struct.new 2)
+  (global (;2;) (ref 2) struct.new 0)
+)
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast/6.print b/tests/snapshots/local/shared-everything-threads/struct.wast/6.print
new file mode 100644
index 00000000..586faeaa
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/struct.wast/6.print
@@ -0,0 +1,3 @@
+(module
+  (type (;0;) (shared(struct (field (ref null (shared any))))))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast/7.print b/tests/snapshots/local/shared-everything-threads/struct.wast/7.print
new file mode 100644
index 00000000..f0fe8e50
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/struct.wast/7.print
@@ -0,0 +1,3 @@
+(module
+  (type (;0;) (struct (field (ref null (shared any)))))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/struct.wast/8.print b/tests/snapshots/local/shared-everything-threads/struct.wast/8.print
new file mode 100644
index 00000000..95f477bf
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/struct.wast/8.print
@@ -0,0 +1,37 @@
+(module
+  (type $i8 (;0;) (shared(struct (field (mut i8)))))
+  (type $i32 (;1;) (shared(struct (field (mut i32)))))
+  (type $unshared (;2;) (struct (field (mut i8))))
+  (type (;3;) (func))
+  (type (;4;) (func (param (ref null $i8))))
+  (type (;5;) (func (param (ref null $i32))))
+  (func (;0;) (type 3)
+    i32.const 0
+    struct.new $i8
+    drop
+  )
+  (func (;1;) (type 3)
+    struct.new_default $i8
+    drop
+  )
+  (func (;2;) (type 4) (param (ref null $i8))
+    local.get 0
+    struct.get_s $i8 0
+    drop
+  )
+  (func (;3;) (type 4) (param (ref null $i8))
+    local.get 0
+    struct.get_u $i8 0
+    drop
+  )
+  (func (;4;) (type 5) (param (ref null $i32))
+    local.get 0
+    struct.get $i32 0
+    drop
+  )
+  (func (;5;) (type 4) (param (ref null $i8))
+    local.get 0
+    i32.const 0
+    struct.set $i8 0
+  )
+)