Repository: bytecodealliance/wasm-tools
Pull request #1800 (2024-09-18 18:48:30)

 Extract "world elaboration" to a separate function 
This commit extracts the process of elaborating a worlds imports/exports
to a dedicated function. This function ensures the transitive closure of
all imports/exports are listed in the world in proper topograhical
order. This additionally validates that the world has a coherent
definition given WIT today, notably that imports don't accidentally try
to use exported types.

This functionality was all present previously during the process of
taking a WIT document and creating `Resolve`. This moves the logic
around and refactors it slightly given its new surroundings. This
reorders a few imports/exports in worlds from what was previously
present but the underlying meaning of worlds should be the same.

The goal of this commit is to enable `Resolve`-mutating transformations
to not need to preserve all these invariants each time a world is
modified. Instead a world can be modified and then this function can be
called to "clean up" the world afterwards.

alexcrichton: The impact of this new function shows up a bit in the second commit here where `importize` is simplified with this operation, but my main motivation is tackling https://github.com/bytecodealliance/wasm-tools/issues/1774 as my work-in-progress to implement that is getting pretty unwieldy trying to maintain these invariants.

Patch:
diff --git a/crates/wasm-encoder/src/component/builder.rs b/crates/wasm-encoder/src/component/builder.rs
index 12e73ca4..af4526a3 100644
--- a/crates/wasm-encoder/src/component/builder.rs
+++ b/crates/wasm-encoder/src/component/builder.rs
@@ -386,6 +386,18 @@ impl ComponentBuilder {
         inc(&mut self.core_funcs)
     }
 
+    /// Declares a new `thread.spawn` intrinsic.
+    pub fn thread_spawn(&mut self, ty: u32) -> u32 {
+        self.canonical_functions().thread_spawn(ty);
+        inc(&mut self.core_funcs)
+    }
+
+    /// Declares a new `thread.hw_concurrency` intrinsic.
+    pub fn thread_hw_concurrency(&mut self) -> u32 {
+        self.canonical_functions().thread_hw_concurrency();
+        inc(&mut self.core_funcs)
+    }
+
     /// Adds a new custom section to this component.
     pub fn custom_section(&mut self, section: &CustomSection<'_>) {
         self.flush();
diff --git a/crates/wasm-encoder/src/component/canonicals.rs b/crates/wasm-encoder/src/component/canonicals.rs
index 340d9ca6..8bb4ad2c 100644
--- a/crates/wasm-encoder/src/component/canonicals.rs
+++ b/crates/wasm-encoder/src/component/canonicals.rs
@@ -144,6 +144,23 @@ impl CanonicalFunctionSection {
         self.num_added += 1;
         self
     }
+
+    /// Defines a function which will spawns a new thread by invoking a shared
+    /// function of type `ty_index`.
+    pub fn thread_spawn(&mut self, ty_index: u32) -> &mut Self {
+        self.bytes.push(0x05);
+        ty_index.encode(&mut self.bytes);
+        self.num_added += 1;
+        self
+    }
+
+    /// Defines a function which will return the number of threads that can be
+    /// expected to execute concurrently.
+    pub fn thread_hw_concurrency(&mut self) -> &mut Self {
+        self.bytes.push(0x06);
+        self.num_added += 1;
+        self
+    }
 }
 
 impl Encode for CanonicalFunctionSection {
diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs
index 771b9c2e..7e0bb666 100644
--- a/crates/wasm-encoder/src/reencode/component.rs
+++ b/crates/wasm-encoder/src/reencode/component.rs
@@ -945,6 +945,13 @@ pub mod component_utils {
                 let resource = reencoder.component_type_index(resource);
                 section.resource_rep(resource);
             }
+            wasmparser::CanonicalFunction::ThreadSpawn { func_ty_index } => {
+                let func_ty = reencoder.type_index(func_ty_index);
+                section.thread_spawn(func_ty);
+            }
+            wasmparser::CanonicalFunction::ThreadHwConcurrency => {
+                section.thread_hw_concurrency();
+            }
         }
         Ok(())
     }
diff --git a/crates/wasmparser/src/readers/component/canonicals.rs b/crates/wasmparser/src/readers/component/canonicals.rs
index fd62ba86..a019abba 100644
--- a/crates/wasmparser/src/readers/component/canonicals.rs
+++ b/crates/wasmparser/src/readers/component/canonicals.rs
@@ -60,6 +60,14 @@ pub enum CanonicalFunction {
         /// The type index of the resource that's being accessed.
         resource: u32,
     },
+    /// A function which spawns a new thread by invoking the shared function.
+    ThreadSpawn {
+        /// The index of the function to spawn.
+        func_ty_index: u32,
+    },
+    /// A function which returns the number of threads that can be expected to
+    /// execute concurrently
+    ThreadHwConcurrency,
 }
 
 /// A reader for the canonical section of a WebAssembly component.
@@ -101,6 +109,10 @@ impl<'a> FromReader<'a> for CanonicalFunction {
             0x04 => CanonicalFunction::ResourceRep {
                 resource: reader.read()?,
             },
+            0x05 => CanonicalFunction::ThreadSpawn {
+                func_ty_index: reader.read()?,
+            },
+            0x06 => CanonicalFunction::ThreadHwConcurrency,
             x => return reader.invalid_leading_byte(x, "canonical function"),
         })
     }
diff --git a/crates/wasmparser/src/readers/core/types.rs b/crates/wasmparser/src/readers/core/types.rs
index 2a600957..f0e82f17 100644
--- a/crates/wasmparser/src/readers/core/types.rs
+++ b/crates/wasmparser/src/readers/core/types.rs
@@ -480,6 +480,18 @@ impl SubType {
         self.composite_type.unwrap_array()
     }
 
+    /// Construct a function `SubType`.
+    pub fn func(signature: FuncType, shared: bool) -> Self {
+        Self {
+            is_final: true,
+            supertype_idx: None,
+            composite_type: CompositeType {
+                inner: CompositeInnerType::Func(signature),
+                shared,
+            },
+        }
+    }
+
     /// Unwrap an `FuncType` or panic.
     ///
     /// Does not check finality or whether there is a supertype.
diff --git a/crates/wasmparser/src/validator.rs b/crates/wasmparser/src/validator.rs
index b6b8be35..3c9bb2c7 100644
--- a/crates/wasmparser/src/validator.rs
+++ b/crates/wasmparser/src/validator.rs
@@ -1217,7 +1217,7 @@ impl Validator {
                 current.funcs.reserve(count as usize);
                 Ok(())
             },
-            |components, types, _, func, offset| {
+            |components, types, features, func, offset| {
                 let current = components.last_mut().unwrap();
                 match func {
                     crate::CanonicalFunction::Lift {
@@ -1244,6 +1244,12 @@ impl Validator {
                     crate::CanonicalFunction::ResourceRep { resource } => {
                         current.resource_rep(resource, types, offset)
                     }
+                    crate::CanonicalFunction::ThreadSpawn { func_ty_index } => {
+                        current.thread_spawn(func_ty_index, types, offset, features)
+                    }
+                    crate::CanonicalFunction::ThreadHwConcurrency => {
+                        current.thread_hw_concurrency(types, offset, features)
+                    }
                 }
             },
         )
diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs
index 7b4524fa..dd2de94f 100644
--- a/crates/wasmparser/src/validator/component.rs
+++ b/crates/wasmparser/src/validator/component.rs
@@ -10,20 +10,20 @@ use super::{
         ModuleType, RecordType, Remapping, ResourceId, TypeAlloc, TypeList, VariantCase,
     },
 };
+use crate::collections::index_map::Entry;
+use crate::limits::*;
 use crate::prelude::*;
+use crate::types::{
+    ComponentAnyTypeId, ComponentCoreModuleTypeId, ComponentCoreTypeId, ComponentDefinedType,
+    ComponentEntityType, Context, CoreInstanceTypeKind, LoweringInfo, Remap, SubtypeCx, TupleType,
+    TypeInfo, VariantType,
+};
 use crate::validator::names::{ComponentName, ComponentNameKind, KebabStr, KebabString};
-use crate::{collections::index_map::Entry, CompositeInnerType};
 use crate::{
-    limits::*,
-    types::{
-        ComponentAnyTypeId, ComponentCoreModuleTypeId, ComponentCoreTypeId, ComponentDefinedType,
-        ComponentEntityType, Context, CoreInstanceTypeKind, LoweringInfo, Remap, SubtypeCx,
-        TupleType, TypeInfo, VariantType,
-    },
     BinaryReaderError, CanonicalOption, ComponentExportName, ComponentExternalKind,
-    ComponentOuterAliasKind, ComponentTypeRef, CompositeType, ExternalKind, FuncType, GlobalType,
-    InstantiationArgKind, MemoryType, RecGroup, Result, SubType, TableType, TypeBounds, ValType,
-    WasmFeatures,
+    ComponentOuterAliasKind, ComponentTypeRef, CompositeInnerType, ExternalKind, FuncType,
+    GlobalType, InstantiationArgKind, MemoryType, PackedIndex, RefType, Result, SubType, TableType,
+    TypeBounds, ValType, WasmFeatures,
 };
 use core::mem;
 
@@ -127,7 +127,7 @@ pub(crate) struct ComponentState {
     /// itself.
     ///
     /// The `Option<ValType>` in this mapping is whether or not the underlying
-    /// reprsentation of the resource is known to this component. Immediately
+    /// representation of the resource is known to this component. Immediately
     /// defined resources, for example, will have `Some(I32)` here. Resources
     /// that come from transitively defined components, for example, will have
     /// `None`. In the type context all entries here are `None`.
@@ -1001,19 +1001,8 @@ 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,
-        };
-
-        let (_is_new, group_id) =
-            types.intern_canonical_rec_group(RecGroup::implicit(offset, lowered_ty));
-        let id = types[group_id].start;
+        let lowered_ty = SubType::func(info.into_func_type(), false);
+        let id = types.intern_sub_type(lowered_ty, offset);
         self.core_funcs.push(id);
 
         Ok(())
@@ -1026,18 +1015,9 @@ 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,
-        };
-        let (_is_new, group_id) =
-            types.intern_canonical_rec_group(RecGroup::implicit(offset, core_ty));
-        let id = types[group_id].start;
+        let func_ty = FuncType::new([rep], [ValType::I32]);
+        let core_ty = SubType::func(func_ty, false);
+        let id = types.intern_sub_type(core_ty, offset);
         self.core_funcs.push(id);
         Ok(())
     }
@@ -1049,18 +1029,9 @@ 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,
-        };
-        let (_is_new, group_id) =
-            types.intern_canonical_rec_group(RecGroup::implicit(offset, core_ty));
-        let id = types[group_id].start;
+        let func_ty = FuncType::new([ValType::I32], []);
+        let core_ty = SubType::func(func_ty, false);
+        let id = types.intern_sub_type(core_ty, offset);
         self.core_funcs.push(id);
         Ok(())
     }
@@ -1072,18 +1043,9 @@ 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,
-        };
-        let (_is_new, group_id) =
-            types.intern_canonical_rec_group(RecGroup::implicit(offset, core_ty));
-        let id = types[group_id].start;
+        let func_ty = FuncType::new([ValType::I32], [rep]);
+        let core_ty = SubType::func(func_ty, false);
+        let id = types.intern_sub_type(core_ty, offset);
         self.core_funcs.push(id);
         Ok(())
     }
@@ -1112,6 +1074,78 @@ impl ComponentState {
         bail!(offset, "type index {} is not a resource type", idx)
     }
 
+    pub fn thread_spawn(
+        &mut self,
+        func_ty_index: u32,
+        types: &mut TypeAlloc,
+        offset: usize,
+        features: &WasmFeatures,
+    ) -> Result<()> {
+        if !features.shared_everything_threads() {
+            bail!(
+                offset,
+                "`thread.spawn` requires the shared-everything-threads proposal"
+            )
+        }
+
+        // Validate the type accepted by `thread.spawn`.
+        let core_type_id = match self.core_type_at(func_ty_index, offset)? {
+            ComponentCoreTypeId::Sub(c) => c,
+            ComponentCoreTypeId::Module(_) => bail!(offset, "expected a core function type"),
+        };
+        let sub_ty = &types[core_type_id];
+        if !sub_ty.composite_type.shared {
+            bail!(offset, "spawn type must be shared");
+        }
+        match &sub_ty.composite_type.inner {
+            CompositeInnerType::Func(func_ty) => {
+                if func_ty.params() != [ValType::I32] {
+                    bail!(
+                        offset,
+                        "spawn function must take a single `i32` argument (currently)"
+                    );
+                }
+                if func_ty.results() != [] {
+                    bail!(offset, "spawn function must not return any values");
+                }
+            }
+            _ => bail!(offset, "spawn type must be a function"),
+        }
+
+        // Insert the core function.
+        let packed_index = PackedIndex::from_id(core_type_id).ok_or_else(|| {
+            format_err!(offset, "implementation limit: too many types in `TypeList`")
+        })?;
+        let start_func_ref = RefType::concrete(true, packed_index);
+        let func_ty = FuncType::new([ValType::Ref(start_func_ref), ValType::I32], [ValType::I32]);
+        let core_ty = SubType::func(func_ty, true);
+        let id = types.intern_sub_type(core_ty, offset);
+        self.core_funcs.push(id);
+
+        Ok(())
+    }
+
+    pub fn thread_hw_concurrency(
+        &mut self,
+        types: &mut TypeAlloc,
+        offset: usize,
+        features: &WasmFeatures,
+    ) -> Result<()> {
+        if !features.shared_everything_threads() {
+            bail!(
+                offset,
+                "`thread.hw_concurrency` requires the shared-everything-threads proposal"
+            )
+        }
+
+        let func_ty = FuncType::new([], [ValType::I32]);
+        let core_ty = SubType::func(func_ty, true);
+        let id = types.intern_sub_type(core_ty, offset);
+        self.core_funcs.push(id);
+
+        Ok(())
+    }
+
     pub fn add_component(&mut self, component: ComponentType, types: &mut TypeAlloc) -> Result<()> {
         let id = types.push_ty(component);
         self.components.push(id);
diff --git a/crates/wasmparser/src/validator/types.rs b/crates/wasmparser/src/validator/types.rs
index aa0ace01..e8701973 100644
--- a/crates/wasmparser/src/validator/types.rs
+++ b/crates/wasmparser/src/validator/types.rs
@@ -2627,6 +2627,14 @@ impl TypeList {
         return (true, rec_group_id);
     }
 
+    /// Helper for interning a sub type as a rec group; see
+    /// [`Self::intern_canonical_rec_group`].
+    pub fn intern_sub_type(&mut self, sub_ty: SubType, offset: usize) -> CoreTypeId {
+        let (_is_new, group_id) =
+            self.intern_canonical_rec_group(RecGroup::implicit(offset, sub_ty));
+        self[group_id].start
+    }
+
     /// Get the `CoreTypeId` for a local index into a rec group.
     pub fn rec_group_local_id(
         &self,
diff --git a/crates/wasmprinter/src/lib.rs b/crates/wasmprinter/src/lib.rs
index 855ccd89..15b34e78 100644
--- a/crates/wasmprinter/src/lib.rs
+++ b/crates/wasmprinter/src/lib.rs
@@ -2377,6 +2377,27 @@ impl Printer<'_, '_> {
                     self.end_group()?;
                     state.core.funcs += 1;
                 }
+                CanonicalFunction::ThreadSpawn {
+                    func_ty_index: func_index,
+                } => {
+                    self.start_group("core func ")?;
+                    self.print_name(&state.core.func_names, state.core.funcs)?;
+                    self.result.write_str(" ")?;
+                    self.start_group("canon thread.spawn ")?;
+                    self.print_idx(&state.core.type_names, func_index)?;
+                    self.end_group()?;
+                    self.end_group()?;
+                    state.core.funcs += 1;
+                }
+                CanonicalFunction::ThreadHwConcurrency => {
+                    self.start_group("core func ")?;
+                    self.print_name(&state.core.func_names, state.core.funcs)?;
+                    self.result.write_str(" ")?;
+                    self.start_group("canon thread.hw_concurrency")?;
+                    self.end_group()?;
+                    self.end_group()?;
+                    state.core.funcs += 1;
+                }
             }
         }
 
diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs
index e3ab6787..0345f07f 100644
--- a/crates/wast/src/component/binary.rs
+++ b/crates/wast/src/component/binary.rs
@@ -347,6 +347,14 @@ impl<'a> Encoder<'a> {
                 self.core_func_names.push(name);
                 self.funcs.resource_rep(info.ty.into());
             }
+            CanonicalFuncKind::ThreadSpawn(info) => {
+                self.core_func_names.push(name);
+                self.funcs.thread_spawn(info.ty.into());
+            }
+            CanonicalFuncKind::ThreadHwConcurrency(_info) => {
+                self.core_func_names.push(name);
+                self.funcs.thread_hw_concurrency();
+            }
         }
 
         self.flush(Some(self.funcs.id()));
diff --git a/crates/wast/src/component/expand.rs b/crates/wast/src/component/expand.rs
index b935e602..58bc1812 100644
--- a/crates/wast/src/component/expand.rs
+++ b/crates/wast/src/component/expand.rs
@@ -263,7 +263,9 @@ impl<'a> Expander<'a> {
             CanonicalFuncKind::Lower(_)
             | CanonicalFuncKind::ResourceNew(_)
             | CanonicalFuncKind::ResourceRep(_)
-            | CanonicalFuncKind::ResourceDrop(_) => {}
+            | CanonicalFuncKind::ResourceDrop(_)
+            | CanonicalFuncKind::ThreadSpawn(_)
+            | CanonicalFuncKind::ThreadHwConcurrency(_) => {}
         }
     }
 
@@ -305,6 +307,20 @@ impl<'a> Expander<'a> {
                 name: func.name,
                 kind: CanonicalFuncKind::ResourceRep(mem::take(info)),
             })),
+            CoreFuncKind::ThreadSpawn(info) => Some(ComponentField::CanonicalFunc(CanonicalFunc {
+                span: func.span,
+                id: func.id,
+                name: func.name,
+                kind: CanonicalFuncKind::ThreadSpawn(mem::take(info)),
+            })),
+            CoreFuncKind::ThreadHwConcurrency(info) => {
+                Some(ComponentField::CanonicalFunc(CanonicalFunc {
+                    span: func.span,
+                    id: func.id,
+                    name: func.name,
+                    kind: CanonicalFuncKind::ThreadHwConcurrency(mem::take(info)),
+                }))
+            }
         }
     }
 
diff --git a/crates/wast/src/component/func.rs b/crates/wast/src/component/func.rs
index 24cbb3eb..ec4d9fc4 100644
--- a/crates/wast/src/component/func.rs
+++ b/crates/wast/src/component/func.rs
@@ -51,6 +51,8 @@ pub enum CoreFuncKind<'a> {
     ResourceNew(CanonResourceNew<'a>),
     ResourceDrop(CanonResourceDrop<'a>),
     ResourceRep(CanonResourceRep<'a>),
+    ThreadSpawn(CanonThreadSpawn<'a>),
+    ThreadHwConcurrency(CanonThreadHwConcurrency),
 }
 
 impl<'a> Parse<'a> for CoreFuncKind<'a> {
@@ -73,6 +75,10 @@ impl<'a> Parse<'a> for CoreFuncKind<'a> {
                 Ok(CoreFuncKind::ResourceDrop(parser.parse()?))
             } else if l.peek::<kw::resource_rep>()? {
                 Ok(CoreFuncKind::ResourceRep(parser.parse()?))
+            } else if l.peek::<kw::thread_spawn>()? {
+                Ok(CoreFuncKind::ThreadSpawn(parser.parse()?))
+            } else if l.peek::<kw::thread_hw_concurrency>()? {
+                Ok(CoreFuncKind::ThreadHwConcurrency(parser.parse()?))
             } else {
                 Err(l.error())
             }
@@ -261,6 +267,9 @@ pub enum CanonicalFuncKind<'a> {
     ResourceNew(CanonResourceNew<'a>),
     ResourceDrop(CanonResourceDrop<'a>),
     ResourceRep(CanonResourceRep<'a>),
+
+    ThreadSpawn(CanonThreadSpawn<'a>),
+    ThreadHwConcurrency(CanonThreadHwConcurrency),
 }
 
 /// Information relating to lifting a core function.
@@ -409,6 +418,48 @@ impl Default for CanonResourceRep<'_> {
     }
 }
 
+/// Information relating to the `thread.spawn` intrinsic.
+#[derive(Debug)]
+pub struct CanonThreadSpawn<'a> {
+    /// The function type that is being spawned.
+    pub ty: Index<'a>,
+}
+
+impl<'a> Parse<'a> for CanonThreadSpawn<'a> {
+    fn parse(parser: Parser<'a>) -> Result<Self> {
+        parser.parse::<kw::thread_spawn>()?;
+
+        Ok(Self {
+            ty: parser.parse()?,
+        })
+    }
+}
+
+impl Default for CanonThreadSpawn<'_> {
+    fn default() -> Self {
+        CanonThreadSpawn {
+            ty: Index::Num(0, Span::from_offset(0)),
+        }
+    }
+}
+
+/// Information relating to the `thread.spawn` intrinsic.
+#[derive(Debug)]
+pub struct CanonThreadHwConcurrency;
+
+impl<'a> Parse<'a> for CanonThreadHwConcurrency {
+    fn parse(parser: Parser<'a>) -> Result<Self> {
+        parser.parse::<kw::thread_hw_concurrency>()?;
+        Ok(Self)
+    }
+}
+
+impl Default for CanonThreadHwConcurrency {
+    fn default() -> Self {
+        Self
+    }
+}
+
 #[derive(Debug)]
 /// Canonical ABI options.
 pub enum CanonOpt<'a> {
diff --git a/crates/wast/src/component/resolve.rs b/crates/wast/src/component/resolve.rs
index 1dfd260a..3a18d857 100644
--- a/crates/wast/src/component/resolve.rs
+++ b/crates/wast/src/component/resolve.rs
@@ -370,6 +370,10 @@ impl<'a> Resolver<'a> {
             CanonicalFuncKind::ResourceDrop(info) => {
                 return self.resolve_ns(&mut info.ty, Ns::Type)
             }
+            CanonicalFuncKind::ThreadSpawn(info) => {
+                return self.resolve_ns(&mut info.ty, Ns::CoreType)
+            }
+            CanonicalFuncKind::ThreadHwConcurrency(_) => return Ok(()),
         };
 
         for opt in opts {
@@ -801,7 +805,9 @@ impl<'a> ComponentState<'a> {
                 CanonicalFuncKind::Lower(_)
                 | CanonicalFuncKind::ResourceNew(_)
                 | CanonicalFuncKind::ResourceRep(_)
-                | CanonicalFuncKind::ResourceDrop(_) => {
+                | CanonicalFuncKind::ResourceDrop(_)
+                | CanonicalFuncKind::ThreadSpawn(_)
+                | CanonicalFuncKind::ThreadHwConcurrency(_) => {
                     self.core_funcs.register(f.id, "core func")?
                 }
             },
diff --git a/crates/wast/src/lib.rs b/crates/wast/src/lib.rs
index c757c0f4..7cd70e0b 100644
--- a/crates/wast/src/lib.rs
+++ b/crates/wast/src/lib.rs
@@ -543,6 +543,8 @@ pub mod kw {
     custom_keyword!(export_info = "export-info");
     custom_keyword!(import_info = "import-info");
     custom_keyword!(thread);
+    custom_keyword!(thread_spawn = "thread.spawn");
+    custom_keyword!(thread_hw_concurrency = "thread.hw_concurrency");
     custom_keyword!(wait);
     custom_keyword!(definition);
 }
diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs
index bd03376f..29050291 100644
--- a/crates/wit-parser/src/resolve.rs
+++ b/crates/wit-parser/src/resolve.rs
@@ -1022,27 +1022,6 @@ package {name} is defined in two different locations:\n\
     /// is intended to be used as part of language tooling when depending on
     /// other components.
     pub fn importize(&mut self, world_id: WorldId) -> Result<()> {
-        // Collect the set of interfaces which are depended on by exports. Also
-        // all imported types are assumed to stay so collect any interfaces
-        // they depend on.
-        let mut live_through_exports = IndexSet::new();
-        for (_, export) in self.worlds[world_id].exports.iter() {
-            if let WorldItem::Interface { id, .. } = export {
-                self.foreach_interface_dep(*id, &mut |id| {
-                    live_through_exports.insert(id);
-                })
-            }
-        }
-        for (_, import) in self.worlds[world_id].imports.iter() {
-            if let WorldItem::Type(ty) = import {
-                if let Some(dep) = self.type_interface_dep(*ty) {
-                    self.foreach_interface_dep(dep, &mut |id| {
-                        live_through_exports.insert(id);
-                    })
-                }
-            }
-        }
-
         // Rename the world to avoid having it get confused with the original
         // name of the world. Add `-importized` to it for now. Precisely how
         // this new world is created may want to be updated over time if this
@@ -1053,44 +1032,33 @@ package {name} is defined in two different locations:\n\
         world.name.push_str("-importized");
         pkg.worlds.insert(world.name.clone(), world_id);
 
-        // Trim all unnecessary imports first.
-        world.imports.retain(|name, item| match (name, item) {
-            // Remove imports which can't be used by import such as:
-            //
-            // * `import foo: interface { .. }`
-            // * `import foo: func();`
-            (WorldKey::Name(_), WorldItem::Interface { .. } | WorldItem::Function(_)) => false,
-
-            // Coarsely say that all top-level types are required to avoid
-            // calculating precise liveness of them right now.
-            (WorldKey::Name(_), WorldItem::Type(_)) => true,
-
-            // Only retain interfaces if they're needed somehow transitively
-            // for the exports.
-            (WorldKey::Interface(id), _) => live_through_exports.contains(id),
+        // Trim all non-type definitions from imports. Types can be used by
+        // exported functions, for example, so they're preserved.
+        world.imports.retain(|_, item| match item {
+            WorldItem::Type(_) => true,
+            _ => false,
         });
 
-        // After all unnecessary imports are gone remove all exports and move
-        // them all to imports, failing if there's an overlap.
         for (name, export) in mem::take(&mut world.exports) {
             match (name.clone(), world.imports.insert(name, export)) {
                 // no previous item? this insertion was ok
                 (_, None) => {}
 
                 // cannot overwrite an import with an export
-                (WorldKey::Name(name), _) => {
+                (WorldKey::Name(name), Some(_)) => {
                     bail!("world export `{name}` conflicts with import of same name");
                 }
 
-                // interface overlap is ok and is always allowed.
-                (WorldKey::Interface(id), Some(WorldItem::Interface { id: other, .. })) => {
-                    assert_eq!(id, other);
-                }
-
+                // Exports already don't overlap each other and the only imports
+                // preserved above were types so this shouldn't be reachable.
                 (WorldKey::Interface(_), _) => unreachable!(),
             }
         }
 
+        // Fill out any missing transitive interface imports by elaborating this
+        // world which does that for us.
+        self.elaborate_world(world_id)?;
+
         #[cfg(debug_assertions)]
         self.assert_valid();
         Ok(())
@@ -1450,6 +1418,7 @@ package {name} is defined in two different locations:\n\
             log::debug!("validating world {}", &world.name);
             assert!(self.packages.get(world.package.unwrap()).is_some());
             assert!(package_worlds[world.package.unwrap().index()].contains(&id));
+            assert!(world.includes.is_empty());
 
             let mut types = HashSet::new();
             for (name, item) in world.imports.iter().chain(world.exports.iter()) {
@@ -1721,6 +1690,263 @@ package {name} is defined in two different locations:\n\
             }
         })
     }
+
+    /// Performs the "elaboration process" necessary for the `world_id`
+    /// specified to ensure that all of its transitive imports are listed.
+    ///
+    /// This function will take the unordered lists of the specified world's
+    /// imports and exports and "elaborate" them to ensure that they're
+    /// topographically sorted where all transitively required interfaces by
+    /// imports, or exports, are listed. This will additionally validate that
+    /// the exports are all valid and present, specifically with the restriction
+    /// noted on `elaborate_world_exports`.
+    ///
+    /// The world is mutated in-place in this `Resolve`.
+    fn elaborate_world(&mut self, world_id: WorldId) -> Result<()> {
+        // First process all imports. This is easier than exports since the only
+        // requirement here is that all interfaces need to be added with a
+        // topological order between them.
+        let mut new_imports = IndexMap::new();
+        let world = &self.worlds[world_id];
+        for (name, item) in world.imports.iter() {
+            match item {
+                // Interfaces get their dependencies added first followed by the
+                // interface itself.
+                WorldItem::Interface { id, stability } => {
+                    self.elaborate_world_import(&mut new_imports, name.clone(), *id, &stability);
+                }
+
+                // Functions are added as-is since their dependence on types in
+                // the world should already be satisfied.
+                WorldItem::Function(_) => {
+                    let prev = new_imports.insert(name.clone(), item.clone());
+                    assert!(prev.is_none());
+                }
+
+                // Types may depend on an interface, in which case a (possibly)
+                // recursive addition of that interface happens here. Afterwards
+                // the type itself can be added safely.
+                WorldItem::Type(id) => {
+                    if let Some(dep) = self.type_interface_dep(*id) {
+                        self.elaborate_world_import(
+                            &mut new_imports,
+                            WorldKey::Interface(dep),
+                            dep,
+                            &self.types[*id].stability,
+                        );
+                    }
+                    let prev = new_imports.insert(name.clone(), item.clone());
+                    assert!(prev.is_none());
+                }
+            }
+        }
+
+        // Exports are trickier than imports, notably to uphold the invariant
+        // required by `elaborate_world_exports`. To do this the exports are
+        // partitioned into interfaces/functions. All functions are added to
+        // the new exports list during this loop but interfaces are all deferred
+        // to be handled in the `elaborate_world_exports` function.
+        let mut new_exports = IndexMap::new();
+        let mut export_interfaces = IndexMap::new();
+        for (name, item) in world.exports.iter() {
+            match item {
+                WorldItem::Interface { id, stability } => {
+                    let prev = export_interfaces.insert(*id, (name.clone(), stability));
+                    assert!(prev.is_none());
+                }
+                WorldItem::Function(_) => {
+                    let prev = new_exports.insert(name.clone(), item.clone());
+                    assert!(prev.is_none());
+                }
+                WorldItem::Type(_) => unreachable!(),
+            }
+        }
+
+        self.elaborate_world_exports(&export_interfaces, &mut new_imports, &mut new_exports)?;
+
+        // And with all that done the world is updated in-place with
+        // imports/exports.
+        log::trace!("imports = {:?}", new_imports);
+        log::trace!("exports = {:?}", new_exports);
+        let world = &mut self.worlds[world_id];
+        world.imports = new_imports;
+        world.exports = new_exports;
+
+        Ok(())
+    }
+
+    fn elaborate_world_import(
+        &self,
+        imports: &mut IndexMap<WorldKey, WorldItem>,
+        key: WorldKey,
+        id: InterfaceId,
+        stability: &Stability,
+    ) {
+        if imports.contains_key(&key) {
+            return;
+        }
+        for dep in self.interface_direct_deps(id) {
+            self.elaborate_world_import(imports, WorldKey::Interface(dep), dep, stability);
+        }
+        let prev = imports.insert(
+            key,
+            WorldItem::Interface {
+                id,
+                stability: stability.clone(),
+            },
+        );
+        assert!(prev.is_none());
+    }
+
+    /// This function adds all of the interfaces in `export_interfaces` to the
+    /// list of exports of the `world` specified.
+    ///
+    /// This method is more involved than adding imports because it is fallible.
+    /// Chiefly what can happen is that the dependencies of all exports must be
+    /// satisfied by other exports or imports, but not both. For example given a
+    /// situation such as:
+    ///
+    /// ```wit
+    /// interface a {
+    ///     type t = u32
+    /// }
+    /// interface b {
+    ///     use a.{t}
+    /// }
+    /// interface c {
+    ///     use a.{t}
+    ///     use b.{t as t2}
+    /// }
+    /// ```
+    ///
+    /// where `c` depends on `b` and `a` where `b` depends on `a`, then the
+    /// purpose of this method is to reject this world:
+    ///
+    /// ```wit
+    /// world foo {
+    ///     export a
+    ///     export c
+    /// }
+    /// ```
+    ///
+    /// The reasoning here is unfortunately subtle and is additionally the
+    /// subject of WebAssembly/component-model#208. Effectively the `c`
+    /// interface depends on `b`, but it's not listed explicitly as an import,
+    /// so it's then implicitly added as an import. This then transitively
+    /// depends on `a` so it's also added as an import. At this point though `c`
+    /// also depends on `a`, and it's also exported, so naively it should depend
+    /// on the export and not implicitly add an import. This means though that
+    /// `c` has access to two copies of `a`, one imported and one exported. This
+    /// is not valid, especially in the face of resource types.
+    ///
+    /// Overall this method is tasked with rejecting the above world by walking
+    /// over all the exports and adding their dependencies. Each dependency is
+    /// recorded with whether it's required to be imported, and then if an
+    /// export is added for something that's required to be an error then the
+    /// operation fails.
+    fn elaborate_world_exports(
+        &self,
+        export_interfaces: &IndexMap<InterfaceId, (WorldKey, &Stability)>,
+        imports: &mut IndexMap<WorldKey, WorldItem>,
+        exports: &mut IndexMap<WorldKey, WorldItem>,
+    ) -> Result<()> {
+        let mut required_imports = HashSet::new();
+        for (id, (key, stability)) in export_interfaces.iter() {
+            let name = self.name_world_key(&key);
+            let ok = add_world_export(
+                self,
+                imports,
+                exports,
+                export_interfaces,
+                &mut required_imports,
+                *id,
+                key,
+                true,
+                stability,
+            );
+            if !ok {
+                bail!(
+                    // FIXME: this is not a great error message and basically no
+                    // one will know what to do when it gets printed. Improving
+                    // this error message, however, is a chunk of work that may
+                    // not be best spent doing this at this time, so I'm writing
+                    // this comment instead.
+                    //
+                    // More-or-less what should happen here is that a "path"
+                    // from this interface to the conflicting interface should
+                    // be printed. It should be explained why an import is being
+                    // injected, why that's conflicting with an export, and
+                    // ideally with a suggestion of "add this interface to the
+                    // export list to fix this error".
+                    //
+                    // That's a lot of info that's not easy to get at without
+                    // more refactoring, so it's left to a future date in the
+                    // hopes that most folks won't actually run into this for
+                    // the time being.
+                    "interface `{name}` transitively depends on an interface in \
+                     incompatible ways",
+                );
+            }
+        }
+        return Ok(());
+
+        fn add_world_export(
+            resolve: &Resolve,
+            imports: &mut IndexMap<WorldKey, WorldItem>,
+            exports: &mut IndexMap<WorldKey, WorldItem>,
+            export_interfaces: &IndexMap<InterfaceId, (WorldKey, &Stability)>,
+            required_imports: &mut HashSet<InterfaceId>,
+            id: InterfaceId,
+            key: &WorldKey,
+            add_export: bool,
+            stability: &Stability,
+        ) -> bool {
+            if exports.contains_key(key) {
+                if add_export {
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+            // If this is an import and it's already in the `required_imports`
+            // set then we can skip it as we've already visited this interface.
+            if !add_export && required_imports.contains(&id) {
+                return true;
+            }
+            let ok = resolve.interface_direct_deps(id).all(|dep| {
+                let key = WorldKey::Interface(dep);
+                let add_export = add_export && export_interfaces.contains_key(&dep);
+                add_world_export(
+                    resolve,
+                    imports,
+                    exports,
+                    export_interfaces,
+                    required_imports,
+                    dep,
+                    &key,
+                    add_export,
+                    stability,
+                )
+            });
+            if !ok {
+                return false;
+            }
+            let item = WorldItem::Interface {
+                id,
+                stability: stability.clone(),
+            };
+            if add_export {
+                if required_imports.contains(&id) {
+                    return false;
+                }
+                exports.insert(key.clone(), item);
+            } else {
+                required_imports.insert(id);
+                imports.insert(key.clone(), item);
+            }
+            true
+        }
+    }
 }
 
 /// Structure returned by [`Resolve::merge`] which contains mappings from
@@ -1933,6 +2159,16 @@ impl Remap {
             let new_id = resolve.worlds.alloc(world);
             assert_eq!(self.worlds.len(), id.index());
             self.worlds.push(Some(new_id));
+
+            resolve.elaborate_world(new_id).with_context(|| {
+                Error::new(
+                    span.span,
+                    format!(
+                        "failed to elaborate world imports/exports of `{}`",
+                        resolve.worlds[new_id].name
+                    ),
+                )
+            })?;
         }
 
         // As with interfaces, now update the ids of world-owned types.
@@ -2039,6 +2275,9 @@ impl Remap {
             }
         }
 
+        #[cfg(debug_assertions)]
+        resolve.assert_valid();
+
         Ok(())
     }
 
@@ -2379,28 +2618,17 @@ impl Remap {
         pkg_id: &PackageId,
         spans: &WorldSpan,
     ) -> Result<()> {
-        // NB: this function is more more complicated than the prior versions
-        // of merging an item because this is the location that elaboration of
-        // imports/exports of a world are fully resolved. With full transitive
-        // knowledge of all interfaces a worlds imports, for example, are
-        // expanded fully to ensure that all transitive items are necessarily
-        // imported.
         assert_eq!(world.imports.len(), spans.imports.len());
         assert_eq!(world.exports.len(), spans.exports.len());
 
-        // First up, process all the `imports` of the world. Note that this
-        // starts by gutting the list of imports stored in `world` to get
-        // rebuilt iteratively below.
-        //
-        // Here each import of an interface is recorded and then additionally
-        // explicitly named imports of interfaces are recorded as well for
-        // determining names later on.
-        let mut import_funcs = Vec::new();
-        let mut import_types = Vec::new();
-        for ((mut name, mut item), span) in mem::take(&mut world.imports)
-            .into_iter()
-            .zip(&spans.imports)
-        {
+        // Rewrite imports/exports with their updated versions. Note that this
+        // may involve updating the key of the imports/exports maps so this
+        // starts by emptying them out and then everything is re-inserted.
+        let imports = mem::take(&mut world.imports).into_iter();
+        let imports = imports.zip(&spans.imports).map(|p| (p, true));
+        let exports = mem::take(&mut world.exports).into_iter();
+        let exports = exports.zip(&spans.exports).map(|p| (p, false));
+        for (((mut name, mut item), span), import) in imports.chain(exports) {
             // Update the `id` eagerly here so `item.stability(..)` below
             // works.
             if let WorldItem::Type(id) = &mut item {
@@ -2420,77 +2648,29 @@ impl Remap {
                 continue;
             }
             self.update_world_key(&mut name, Some(*span))?;
-            match item {
-                WorldItem::Interface { id, stability } => {
-                    let id = self.map_interface(id, Some(*span))?;
-                    self.add_world_import(resolve, world, name, id, &stability);
+            match &mut item {
+                WorldItem::Interface { id, .. } => {
+                    *id = self.map_interface(*id, Some(*span))?;
                 }
-                WorldItem::Function(mut f) => {
-                    self.update_function(resolve, &mut f, Some(*span))?;
-                    import_funcs.push((name.unwrap_name(), f, *span));
+                WorldItem::Function(f) => {
+                    self.update_function(resolve, f, Some(*span))?;
                 }
-                WorldItem::Type(id) => {
-                    import_types.push((name.unwrap_name(), id, *span));
+                WorldItem::Type(_) => {
+                    // already mapped above
                 }
             }
-        }
 
-        for (_name, id, _span) in import_types.iter() {
-            if let TypeDefKind::Type(Type::Id(other)) = resolve.types[*id].kind {
-                if let TypeOwner::Interface(owner) = resolve.types[other].owner {
-                    let name = WorldKey::Interface(owner);
-                    self.add_world_import(
-                        resolve,
-                        world,
-                        name,
-                        owner,
-                        &resolve.types[*id].stability,
-                    );
-                }
-            }
-        }
-
-        let mut export_funcs = Vec::new();
-        let mut export_interfaces = IndexMap::new();
-        for ((mut name, item), span) in mem::take(&mut world.exports)
-            .into_iter()
-            .zip(&spans.exports)
-        {
-            let stability = item.stability(resolve);
-            if !resolve
-                .include_stability(stability, pkg_id)
-                .with_context(|| {
-                    format!(
-                        "failed to process feature gate for exported item [{}] in package [{}]",
-                        resolve.name_world_key(&name),
-                        resolve.packages[*pkg_id].name,
-                    )
-                })?
-            {
-                continue;
-            }
-            self.update_world_key(&mut name, Some(*span))?;
-            match item {
-                WorldItem::Interface { id, stability } => {
-                    let id = self.map_interface(id, Some(*span))?;
-                    let prev = export_interfaces.insert(id, (name, *span, stability));
-                    assert!(prev.is_none());
-                }
-                WorldItem::Function(mut f) => {
-                    self.update_function(resolve, &mut f, Some(*span))?;
-                    let name = match name {
-                        WorldKey::Name(name) => name,
-                        WorldKey::Interface(_) => unreachable!(),
-                    };
-                    export_funcs.push((name, f, *span));
-                }
-                WorldItem::Type(_) => unreachable!(),
-            }
+            let dst = if import {
+                &mut world.imports
+            } else {
+                &mut world.exports
+            };
+            let prev = dst.insert(name, item);
+            assert!(prev.is_none());
         }
 
-        self.add_world_exports(resolve, world, &export_interfaces)?;
-
-        // Resolve all includes of the world
+        // Resolve all `include` statements of the world which will add more
+        // entries to the imports/exports list for this world.
         assert_eq!(world.includes.len(), spans.includes.len());
         let includes = mem::take(&mut world.includes);
         let include_names = mem::take(&mut world.include_names);
@@ -2514,71 +2694,6 @@ impl Remap {
             self.resolve_include(world, include_world, names, *span, resolve)?;
         }
 
-        for (name, id, span) in import_types {
-            let prev = world
-                .imports
-                .insert(WorldKey::Name(name.clone()), WorldItem::Type(id));
-            if prev.is_some() {
-                bail!(Error::new(
-                    span,
-                    format!("export of type `{name}` shadows previously imported interface"),
-                ))
-            }
-        }
-
-        for (name, func, span) in import_funcs {
-            let prev = world
-                .imports
-                .insert(WorldKey::Name(name.clone()), WorldItem::Function(func));
-            if prev.is_some() {
-                bail!(Error::new(
-                    span,
-                    format!("import of function `{name}` shadows previously imported interface"),
-                ))
-            }
-        }
-
-        for (name, func, span) in export_funcs {
-            let prev = world
-                .exports
-                .insert(WorldKey::Name(name.clone()), WorldItem::Function(func));
-            if prev.is_some() {
-                bail!(Error::new(
-                    span,
-                    format!("export of function `{name}` shadows previously exported interface"),
-                ))
-            }
-        }
-
-        // After all that sort functions in exports to come before interfaces in
-        // exports. This is not strictly required for correctness but make
-        // iterating over a world much easier for consumers. Exported functions
-        // are guaranteed to use types from either imported interfaces or
-        // imported types into the world itself. Currently there is no means by
-        // which an export function, at the root, can use types from any other
-        // exported interfaces (can't be modeled syntactically in WIT). This
-        // means that by placing all functions first it guarantees that visitors
-        // which visit imports first then exports will walk over types and
-        // references in the order of what they're actually using.
-        //
-        // For example if an interface is both imported and exported and an
-        // exported function uses a type from that interface, then a visitor
-        // should visit the imported interface, then the exported function, then
-        // the exported interface. That way tables about "where was this type
-        // defined" will be correct as the last-inserted item will be used and
-        // correctly account for this.
-        world.exports.sort_by(|_, a, _, b| {
-            let rank = |item: &WorldItem| match item {
-                WorldItem::Type(_) => unreachable!(),
-                WorldItem::Function(_) => 0,
-                WorldItem::Interface { .. } => 1,
-            };
-            rank(a).cmp(&rank(b))
-        });
-
-        log::trace!("imports = {:?}", world.imports);
-        log::trace!("exports = {:?}", world.exports);
-
         Ok(())
     }
 
@@ -2592,179 +2707,6 @@ impl Remap {
         Ok(())
     }
 
-    fn add_world_import(
-        &self,
-        resolve: &Resolve,
-        world: &mut World,
-        key: WorldKey,
-        id: InterfaceId,
-        stability: &Stability,
-    ) {
-        if world.imports.contains_key(&key) {
-            return;
-        }
-        for dep in resolve.interface_direct_deps(id) {
-            self.add_world_import(resolve, world, WorldKey::Interface(dep), dep, stability);
-        }
-        let prev = world.imports.insert(
-            key,
-            WorldItem::Interface {
-                id,
-                stability: stability.clone(),
-            },
-        );
-        assert!(prev.is_none());
-    }
-
-    /// This function adds all of the interfaces in `export_interfaces` to the
-    /// list of exports of the `world` specified.
-    ///
-    /// This method is more involved than adding imports because it is fallible.
-    /// Chiefly what can happen is that the dependencies of all exports must be
-    /// satisfied by other exports or imports, but not both. For example given a
-    /// situation such as:
-    ///
-    /// ```wit
-    /// interface a {
-    ///     type t = u32
-    /// }
-    /// interface b {
-    ///     use a.{t}
-    /// }
-    /// interface c {
-    ///     use a.{t}
-    ///     use b.{t as t2}
-    /// }
-    /// ```
-    ///
-    /// where `c` depends on `b` and `a` where `b` depends on `a`, then the
-    /// purpose of this method is to reject this world:
-    ///
-    /// ```wit
-    /// world foo {
-    ///     export a
-    ///     export c
-    /// }
-    /// ```
-    ///
-    /// The reasoning here is unfortunately subtle and is additionally the
-    /// subject of WebAssembly/component-model#208. Effectively the `c`
-    /// interface depends on `b`, but it's not listed explicitly as an import,
-    /// so it's then implicitly added as an import. This then transitively
-    /// depends on `a` so it's also added as an import. At this point though `c`
-    /// also depends on `a`, and it's also exported, so naively it should depend
-    /// on the export and not implicitly add an import. This means though that
-    /// `c` has access to two copies of `a`, one imported and one exported. This
-    /// is not valid, especially in the face of resource types.
-    ///
-    /// Overall this method is tasked with rejecting the above world by walking
-    /// over all the exports and adding their dependencies. Each dependency is
-    /// recorded with whether it's required to be imported, and then if an
-    /// export is added for something that's required to be an error then the
-    /// operation fails.
-    fn add_world_exports(
-        &self,
-        resolve: &Resolve,
-        world: &mut World,
-        export_interfaces: &IndexMap<InterfaceId, (WorldKey, Span, Stability)>,
-    ) -> Result<()> {
-        let mut required_imports = HashSet::new();
-        for (id, (key, span, stability)) in export_interfaces.iter() {
-            let ok = add_world_export(
-                resolve,
-                world,
-                export_interfaces,
-                &mut required_imports,
-                *id,
-                key,
-                true,
-                &stability,
-            );
-            if !ok {
-                bail!(Error::new(
-                    *span,
-                    // FIXME: this is not a great error message and basically no
-                    // one will know what to do when it gets printed. Improving
-                    // this error message, however, is a chunk of work that may
-                    // not be best spent doing this at this time, so I'm writing
-                    // this comment instead.
-                    //
-                    // More-or-less what should happen here is that a "path"
-                    // from this interface to the conflicting interface should
-                    // be printed. It should be explained why an import is being
-                    // injected, why that's conflicting with an export, and
-                    // ideally with a suggestion of "add this interface to the
-                    // export list to fix this error".
-                    //
-                    // That's a lot of info that's not easy to get at without
-                    // more refactoring, so it's left to a future date in the
-                    // hopes that most folks won't actually run into this for
-                    // the time being.
-                    format!(
-                        "interface transitively depends on an interface in \
-                         incompatible ways",
-                    ),
-                ));
-            }
-        }
-        return Ok(());
-
-        fn add_world_export(
-            resolve: &Resolve,
-            world: &mut World,
-            export_interfaces: &IndexMap<InterfaceId, (WorldKey, Span, Stability)>,
-            required_imports: &mut HashSet<InterfaceId>,
-            id: InterfaceId,
-            key: &WorldKey,
-            add_export: bool,
-            stability: &Stability,
-        ) -> bool {
-            if world.exports.contains_key(key) {
-                if add_export {
-                    return true;
-                } else {
-                    return false;
-                }
-            }
-            // If this is an import and it's already in the `required_imports`
-            // set then we can skip it as we've already visited this interface.
-            if !add_export && required_imports.contains(&id) {
-                return true;
-            }
-            let ok = resolve.interface_direct_deps(id).all(|dep| {
-                let key = WorldKey::Interface(dep);
-                let add_export = add_export && export_interfaces.contains_key(&dep);
-                add_world_export(
-                    resolve,
-                    world,
-                    export_interfaces,
-                    required_imports,
-                    dep,
-                    &key,
-                    add_export,
-                    stability,
-                )
-            });
-            if !ok {
-                return false;
-            }
-            let item = WorldItem::Interface {
-                id,
-                stability: stability.clone(),
-            };
-            if add_export {
-                if required_imports.contains(&id) {
-                    return false;
-                }
-                world.exports.insert(key.clone(), item);
-            } else {
-                required_imports.insert(id);
-                world.imports.insert(key.clone(), item);
-            }
-            true
-        }
-    }
-
     fn resolve_include(
         &self,
         world: &mut World,
diff --git a/src/bin/wasm-tools/dump.rs b/src/bin/wasm-tools/dump.rs
index 7c3539ec..45c3038b 100644
--- a/src/bin/wasm-tools/dump.rs
+++ b/src/bin/wasm-tools/dump.rs
@@ -417,7 +417,9 @@ impl<'a> Dump<'a> {
                             CanonicalFunction::Lower { .. }
                             | CanonicalFunction::ResourceNew { .. }
                             | CanonicalFunction::ResourceDrop { .. }
-                            | CanonicalFunction::ResourceRep { .. } => {
+                            | CanonicalFunction::ResourceRep { .. }
+                            | CanonicalFunction::ThreadSpawn { .. }
+                            | CanonicalFunction::ThreadHwConcurrency => {
                                 ("core func", &mut i.core_funcs)
                             }
                         };


Test patch:
diff --git a/crates/wit-component/tests/interfaces/resources.wat b/crates/wit-component/tests/interfaces/resources.wat
index c4a8a9d3..3d07b806 100644
--- a/crates/wit-component/tests/interfaces/resources.wat
+++ b/crates/wit-component/tests/interfaces/resources.wat
@@ -100,20 +100,6 @@
       (type (;0;)
         (component
           (type (;0;)
-            (instance
-              (export (;0;) "a" (type (sub resource)))
-              (type (;1;) (own 0))
-              (type (;2;) (func (result 1)))
-              (export (;0;) "[constructor]a" (func (type 2)))
-              (type (;3;) (func))
-              (export (;1;) "[static]a.b" (func (type 3)))
-              (type (;4;) (borrow 0))
-              (type (;5;) (func (param "self" 4)))
-              (export (;2;) "[method]a.c" (func (type 5)))
-            )
-          )
-          (import "anon" (instance (;0;) (type 0)))
-          (type (;1;)
             (instance
               (export (;0;) "bar" (type (sub resource)))
               (export (;1;) "t" (type (eq 0)))
@@ -131,14 +117,14 @@
               (export (;3;) "a" (func (type 9)))
             )
           )
-          (import "foo:bar/foo" (instance (;1;) (type 1)))
-          (alias export 1 "bar" (type (;2;)))
-          (alias export 1 "t" (type (;3;)))
-          (type (;4;)
+          (import "foo:bar/foo" (instance (;0;) (type 0)))
+          (alias export 0 "bar" (type (;1;)))
+          (alias export 0 "t" (type (;2;)))
+          (type (;3;)
             (instance
-              (alias outer 1 2 (type (;0;)))
+              (alias outer 1 1 (type (;0;)))
               (export (;1;) "bar" (type (eq 0)))
-              (alias outer 1 3 (type (;2;)))
+              (alias outer 1 2 (type (;2;)))
               (export (;3;) "t" (type (eq 2)))
               (type (;4;) (own 1))
               (type (;5;) (func (param "x" 4) (result 4)))
@@ -148,15 +134,29 @@
               (export (;1;) "b" (func (type 7)))
             )
           )
-          (import "foo:bar/baz" (instance (;2;) (type 4)))
-          (alias export 2 "bar" (type (;5;)))
-          (import "bar" (type (;6;) (eq 5)))
-          (import "a" (type (;7;) (sub resource)))
-          (type (;8;) (own 7))
+          (import "foo:bar/baz" (instance (;1;) (type 3)))
+          (alias export 1 "bar" (type (;4;)))
+          (import "bar" (type (;5;) (eq 4)))
+          (import "a" (type (;6;) (sub resource)))
+          (type (;7;)
+            (instance
+              (export (;0;) "a" (type (sub resource)))
+              (type (;1;) (own 0))
+              (type (;2;) (func (result 1)))
+              (export (;0;) "[constructor]a" (func (type 2)))
+              (type (;3;) (func))
+              (export (;1;) "[static]a.b" (func (type 3)))
+              (type (;4;) (borrow 0))
+              (type (;5;) (func (param "self" 4)))
+              (export (;2;) "[method]a.c" (func (type 5)))
+            )
+          )
+          (import "anon" (instance (;2;) (type 7)))
+          (type (;8;) (own 6))
           (type (;9;) (func (result 8)))
           (import "[constructor]a" (func (;0;) (type 9)))
           (export (;1;) "x" (func (type 9)))
-          (type (;10;) (own 6))
+          (type (;10;) (own 5))
           (type (;11;) (func (result 10)))
           (export (;2;) "y" (func (type 11)))
         )
diff --git a/crates/wit-component/tests/interfaces/resources.wit.print b/crates/wit-component/tests/interfaces/resources.wit.print
index c52d6744..6f386b6d 100644
--- a/crates/wit-component/tests/interfaces/resources.wit.print
+++ b/crates/wit-component/tests/interfaces/resources.wit.print
@@ -50,6 +50,8 @@ interface implicit-own-handles2 {
 }
 
 world some-world {
+  import foo;
+  import baz;
   import anon: interface {
     resource a {
       constructor();
@@ -57,8 +59,6 @@ world some-world {
       c: func();
     }
   }
-  import foo;
-  import baz;
   use baz.{bar};
 
   resource a {
diff --git a/crates/wit-component/tests/interfaces/world-top-level.wat b/crates/wit-component/tests/interfaces/world-top-level.wat
index 4afcebfc..347066ec 100644
--- a/crates/wit-component/tests/interfaces/world-top-level.wat
+++ b/crates/wit-component/tests/interfaces/world-top-level.wat
@@ -3,15 +3,15 @@
     (component
       (type (;0;)
         (component
-          (type (;0;)
+          (type (;0;) (func))
+          (import "foo" (func (;0;) (type 0)))
+          (type (;1;) (func (param "arg" u32)))
+          (import "bar" (func (;1;) (type 1)))
+          (type (;2;)
             (instance)
           )
-          (import "some-interface" (instance (;0;) (type 0)))
-          (type (;1;) (func))
-          (import "foo" (func (;0;) (type 1)))
-          (type (;2;) (func (param "arg" u32)))
-          (import "bar" (func (;1;) (type 2)))
-          (export (;2;) "foo2" (func (type 1)))
+          (import "some-interface" (instance (;0;) (type 2)))
+          (export (;2;) "foo2" (func (type 0)))
           (type (;3;) (func (result u32)))
           (export (;3;) "bar2" (func (type 3)))
           (type (;4;)
diff --git a/crates/wit-component/tests/interfaces/world-top-level.wit.print b/crates/wit-component/tests/interfaces/world-top-level.wit.print
index b28325ef..5308f654 100644
--- a/crates/wit-component/tests/interfaces/world-top-level.wit.print
+++ b/crates/wit-component/tests/interfaces/world-top-level.wit.print
@@ -1,10 +1,10 @@
 package foo:foo;
 
 world foo {
-  import some-interface: interface {
-  }
   import foo: func();
   import bar: func(arg: u32);
+  import some-interface: interface {
+  }
 
   export foo2: func();
   export bar2: func() -> u32;
diff --git a/crates/wit-parser/tests/ui/parse-fail/import-and-export1.wit.result b/crates/wit-parser/tests/ui/parse-fail/import-and-export1.wit.result
index 0726c2aa..2642cf4c 100644
--- a/crates/wit-parser/tests/ui/parse-fail/import-and-export1.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/import-and-export1.wit.result
@@ -1,5 +1,5 @@
-interface transitively depends on an interface in incompatible ways
-     --> tests/ui/parse-fail/import-and-export1.wit:16:10
+failed to elaborate world imports/exports of `test`
+     --> tests/ui/parse-fail/import-and-export1.wit:12:7
       |
-   16 |   export i3;
-      |          ^-
\ No newline at end of file
+   12 | world test {
+      |       ^---: interface `foo:foo/i3` transitively depends on an interface in incompatible ways
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/import-and-export2.wit.result b/crates/wit-parser/tests/ui/parse-fail/import-and-export2.wit.result
index 85777bfc..76406d79 100644
--- a/crates/wit-parser/tests/ui/parse-fail/import-and-export2.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/import-and-export2.wit.result
@@ -1,5 +1,5 @@
-interface transitively depends on an interface in incompatible ways
-     --> tests/ui/parse-fail/import-and-export2.wit:14:10
+failed to elaborate world imports/exports of `baz`
+     --> tests/ui/parse-fail/import-and-export2.wit:11:7
       |
-   14 |   export anon: interface {
-      |          ^---
\ No newline at end of file
+   11 | world baz {
+      |       ^--: interface `anon` transitively depends on an interface in incompatible ways
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/import-and-export3.wit.result b/crates/wit-parser/tests/ui/parse-fail/import-and-export3.wit.result
index 7e20ce59..857a9279 100644
--- a/crates/wit-parser/tests/ui/parse-fail/import-and-export3.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/import-and-export3.wit.result
@@ -1,5 +1,5 @@
-interface transitively depends on an interface in incompatible ways
-     --> tests/ui/parse-fail/import-and-export3.wit:36:10
+failed to elaborate world imports/exports of `test`
+     --> tests/ui/parse-fail/import-and-export3.wit:33:7
       |
-   36 |   export i3;
-      |          ^-
\ No newline at end of file
+   33 | world test {
+      |       ^---: interface `foo:foo/i3` transitively depends on an interface in incompatible ways
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/import-and-export4.wit.result b/crates/wit-parser/tests/ui/parse-fail/import-and-export4.wit.result
index b1590273..9028ce73 100644
--- a/crates/wit-parser/tests/ui/parse-fail/import-and-export4.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/import-and-export4.wit.result
@@ -1,5 +1,5 @@
-interface transitively depends on an interface in incompatible ways
-     --> tests/ui/parse-fail/import-and-export4.wit:43:10
+failed to elaborate world imports/exports of `test`
+     --> tests/ui/parse-fail/import-and-export4.wit:40:7
       |
-   43 |   export i3;
-      |          ^-
\ No newline at end of file
+   40 | world test {
+      |       ^---: interface `foo:foo/i3` transitively depends on an interface in incompatible ways
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/parse-fail/import-and-export5.wit.result b/crates/wit-parser/tests/ui/parse-fail/import-and-export5.wit.result
index 6e86b74c..4bb5face 100644
--- a/crates/wit-parser/tests/ui/parse-fail/import-and-export5.wit.result
+++ b/crates/wit-parser/tests/ui/parse-fail/import-and-export5.wit.result
@@ -1,5 +1,5 @@
-interface transitively depends on an interface in incompatible ways
-     --> tests/ui/parse-fail/import-and-export5.wit:13:10
+failed to elaborate world imports/exports of `w`
+     --> tests/ui/parse-fail/import-and-export5.wit:12:7
       |
-   13 |   export anon: interface {
-      |          ^---
\ No newline at end of file
+   12 | world w {
+      |       ^: interface `anon` transitively depends on an interface in incompatible ways
\ No newline at end of file
diff --git a/crates/wit-parser/tests/ui/since-and-unstable.wit.json b/crates/wit-parser/tests/ui/since-and-unstable.wit.json
index e2126eaa..a4b75947 100644
--- a/crates/wit-parser/tests/ui/since-and-unstable.wit.json
+++ b/crates/wit-parser/tests/ui/since-and-unstable.wit.json
@@ -25,26 +25,6 @@
     {
       "name": "in-a-world",
       "imports": {
-        "y": {
-          "interface": {
-            "id": 6,
-            "stability": {
-              "stable": {
-                "since": "1.0.0"
-              }
-            }
-          }
-        },
-        "interface-4": {
-          "interface": {
-            "id": 4,
-            "stability": {
-              "stable": {
-                "since": "1.0.0"
-              }
-            }
-          }
-        },
         "t1": {
           "type": 11
         },
@@ -79,6 +59,26 @@
             }
           }
         },
+        "y": {
+          "interface": {
+            "id": 6,
+            "stability": {
+              "stable": {
+                "since": "1.0.0"
+              }
+            }
+          }
+        },
+        "interface-4": {
+          "interface": {
+            "id": 4,
+            "stability": {
+              "stable": {
+                "since": "1.0.0"
+              }
+            }
+          }
+        },
         "[constructor]t7": {
           "function": {
             "name": "[constructor]t7",
diff --git a/tests/cli/importize.wit.simple-toplevel.stdout b/tests/cli/importize.wit.simple-toplevel.stdout
index fa9ccaa8..4b279899 100644
--- a/tests/cli/importize.wit.simple-toplevel.stdout
+++ b/tests/cli/importize.wit.simple-toplevel.stdout
@@ -71,9 +71,9 @@ world fail1 {
 }
 world trim-imports {
   import a;
+  import foo: func();
   import bar: interface {
   }
-  import foo: func();
 
   type t = u32;
 
diff --git a/tests/cli/importize.wit.simple.stdout b/tests/cli/importize.wit.simple.stdout
index 12023766..74df901e 100644
--- a/tests/cli/importize.wit.simple.stdout
+++ b/tests/cli/importize.wit.simple.stdout
@@ -74,9 +74,9 @@ world fail1 {
 }
 world trim-imports {
   import a;
+  import foo: func();
   import bar: interface {
   }
-  import foo: func();
 
   type t = u32;
 
diff --git a/tests/cli/importize.wit.toplevel-deps.stdout b/tests/cli/importize.wit.toplevel-deps.stdout
index 1281c65c..05b41d4c 100644
--- a/tests/cli/importize.wit.toplevel-deps.stdout
+++ b/tests/cli/importize.wit.toplevel-deps.stdout
@@ -65,9 +65,9 @@ world fail1 {
 }
 world trim-imports {
   import a;
+  import foo: func();
   import bar: interface {
   }
-  import foo: func();
 
   type t = u32;
 
@@ -80,8 +80,8 @@ world tricky-import {
   export f: func() -> t;
 }
 world toplevel-deps-importized {
-  import something-else-dep;
   import bar: func() -> s;
+  import something-else-dep;
   import something-else: interface {
     use something-else-dep.{t};
 
diff --git a/tests/cli/importize.wit.tricky-import.stdout b/tests/cli/importize.wit.tricky-import.stdout
index af547e26..c237d71a 100644
--- a/tests/cli/importize.wit.tricky-import.stdout
+++ b/tests/cli/importize.wit.tricky-import.stdout
@@ -77,9 +77,9 @@ world fail1 {
 }
 world trim-imports {
   import a;
+  import foo: func();
   import bar: interface {
   }
-  import foo: func();
 
   type t = u32;
 
diff --git a/tests/cli/importize.wit.with-deps.stdout b/tests/cli/importize.wit.with-deps.stdout
index 64c3f70b..96e7d320 100644
--- a/tests/cli/importize.wit.with-deps.stdout
+++ b/tests/cli/importize.wit.with-deps.stdout
@@ -71,9 +71,9 @@ world fail1 {
 }
 world trim-imports {
   import a;
+  import foo: func();
   import bar: interface {
   }
-  import foo: func();
 
   type t = u32;
 
diff --git a/tests/cli/wit-stability-in-binary-format.wit.stdout b/tests/cli/wit-stability-in-binary-format.wit.stdout
index f6dd5adc..45db6e8b 100644
--- a/tests/cli/wit-stability-in-binary-format.wit.stdout
+++ b/tests/cli/wit-stability-in-binary-format.wit.stdout
@@ -25,10 +25,10 @@ world w {
   @since(version = 1.0.0)
   import foo;
   @since(version = 1.0.0)
+  import f: func();
+  @since(version = 1.0.0)
   import a: interface {
   }
-  @since(version = 1.0.0)
-  import f: func();
 
   @since(version = 1.0.0)
   type t = u32;
diff --git a/tests/cli/world-merging-add-imports.wit.stdout b/tests/cli/world-merging-add-imports.wit.stdout
index 04998711..6f0064d4 100644
--- a/tests/cli/world-merging-add-imports.wit.stdout
+++ b/tests/cli/world-merging-add-imports.wit.stdout
@@ -1,9 +1,9 @@
 package root:root;
 
 world root {
+  import a:b/b;
   import a: interface {
   }
-  import a:b/b;
   import c: func() -> t;
   use a:b/b.{t};
 }
@@ -14,9 +14,9 @@ package a:b {
   world into {
   }
   world %from {
+    import b;
     import a: interface {
     }
-    import b;
     import c: func() -> t;
     use b.{t};
   }
diff --git a/tests/cli/world-merging-same-imports.wit.stdout b/tests/cli/world-merging-same-imports.wit.stdout
index 6ad405c9..90090844 100644
--- a/tests/cli/world-merging-same-imports.wit.stdout
+++ b/tests/cli/world-merging-same-imports.wit.stdout
@@ -1,9 +1,9 @@
 package root:root;
 
 world root {
+  import a:b/b;
   import a: interface {
   }
-  import a:b/b;
   import c: func();
   use a:b/b.{t};
 }
@@ -12,16 +12,16 @@ package a:b {
     type t = u32;
   }
   world into {
+    import b;
     import a: interface {
     }
-    import b;
     import c: func();
     use b.{t};
   }
   world %from {
+    import b;
     import a: interface {
     }
-    import b;
     import c: func();
     use b.{t};
   }
diff --git a/tests/local/missing-features/component-model/shared-everything-threads-builtins.wast b/tests/local/missing-features/component-model/shared-everything-threads-builtins.wast
new file mode 100644
index 00000000..2468bc28
--- /dev/null
+++ b/tests/local/missing-features/component-model/shared-everything-threads-builtins.wast
@@ -0,0 +1,13 @@
+(assert_invalid
+  (component
+    ;; Refer to a non-existent function type (i.e., 0); validation for
+    ;; `thread.spawn` happens first.
+    (core func $spawn (canon thread.spawn 0))
+  )
+  "`thread.spawn` requires the shared-everything-threads proposal")
+
+(assert_invalid
+  (component
+    (core func $concurrency (canon thread.hw_concurrency))
+  )
+  "`thread.hw_concurrency` requires the shared-everything-threads proposal")
diff --git a/tests/local/shared-everything-threads/builtins.wast b/tests/local/shared-everything-threads/builtins.wast
new file mode 100644
index 00000000..7f5e1e1c
--- /dev/null
+++ b/tests/local/shared-everything-threads/builtins.wast
@@ -0,0 +1,42 @@
+;; Styled after ../component-model/resources.wast
+
+(component
+  (core type $start (shared (func (param $context i32))))
+  (core func $spawn (canon thread.spawn $start))
+  (core func $concurrency (canon thread.hw_concurrency))
+)
+
+(component
+  (core type $start (shared (func (param $context i32))))
+  (core func $spawn (canon thread.spawn $start))
+  (core func $concurrency (canon thread.hw_concurrency))
+
+  (core module $m
+    (type $st (shared (func (param $context i32))))
+    (import "" "spawn" (func (param (ref null $st)) (param i32) (result i32)))
+    (import "" "concurrency" (func (result i32)))
+  )
+
+  (core instance (instantiate $m
+    (with "" (instance
+      (export "spawn" (func $spawn))
+      (export "concurrency" (func $concurrency))
+    ))
+  ))
+)
+
+(assert_invalid
+  (component
+    (core type $start (func))
+    (core func $spawn (canon thread.spawn $start))
+  )
+  "spawn type must be shared"
+)
+
+(assert_invalid
+  (component
+    (core type $start (shared (func)))
+    (core func $spawn (canon thread.spawn $start))
+  )
+  "spawn function must take a single `i32` argument (currently)"
+)
diff --git a/tests/snapshots/local/missing-features/component-model/shared-everything-threads-builtins.wast.json b/tests/snapshots/local/missing-features/component-model/shared-everything-threads-builtins.wast.json
new file mode 100644
index 00000000..81b0f5ec
--- /dev/null
+++ b/tests/snapshots/local/missing-features/component-model/shared-everything-threads-builtins.wast.json
@@ -0,0 +1,19 @@
+{
+  "source_filename": "tests/local/missing-features/component-model/shared-everything-threads-builtins.wast",
+  "commands": [
+    {
+      "type": "assert_invalid",
+      "line": 2,
+      "filename": "shared-everything-threads-builtins.0.wasm",
+      "module_type": "binary",
+      "text": "`thread.spawn` requires the shared-everything-threads proposal"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 10,
+      "filename": "shared-everything-threads-builtins.1.wasm",
+      "module_type": "binary",
+      "text": "`thread.hw_concurrency` requires the shared-everything-threads proposal"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/builtins.wast.json b/tests/snapshots/local/shared-everything-threads/builtins.wast.json
new file mode 100644
index 00000000..11946d73
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/builtins.wast.json
@@ -0,0 +1,31 @@
+{
+  "source_filename": "tests/local/shared-everything-threads/builtins.wast",
+  "commands": [
+    {
+      "type": "module",
+      "line": 3,
+      "filename": "builtins.0.wasm",
+      "module_type": "binary"
+    },
+    {
+      "type": "module",
+      "line": 9,
+      "filename": "builtins.1.wasm",
+      "module_type": "binary"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 29,
+      "filename": "builtins.2.wasm",
+      "module_type": "binary",
+      "text": "spawn type must be shared"
+    },
+    {
+      "type": "assert_invalid",
+      "line": 37,
+      "filename": "builtins.3.wasm",
+      "module_type": "binary",
+      "text": "spawn function must take a single `i32` argument (currently)"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/tests/snapshots/local/shared-everything-threads/builtins.wast/0.print b/tests/snapshots/local/shared-everything-threads/builtins.wast/0.print
new file mode 100644
index 00000000..4c37d848
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/builtins.wast/0.print
@@ -0,0 +1,5 @@
+(component
+  (core type $start (;0;) (shared(func (param i32))))
+  (core func $spawn (;0;) (canon thread.spawn $start))
+  (core func $concurrency (;1;) (canon thread.hw_concurrency))
+)
diff --git a/tests/snapshots/local/shared-everything-threads/builtins.wast/1.print b/tests/snapshots/local/shared-everything-threads/builtins.wast/1.print
new file mode 100644
index 00000000..aa850861
--- /dev/null
+++ b/tests/snapshots/local/shared-everything-threads/builtins.wast/1.print
@@ -0,0 +1,20 @@
+(component
+  (core type $start (;0;) (shared(func (param i32))))
+  (core func $spawn (;0;) (canon thread.spawn $start))
+  (core func $concurrency (;1;) (canon thread.hw_concurrency))
+  (core module $m (;0;)
+    (type $st (;0;) (shared(func (param i32))))
+    (type (;1;) (func (param (ref null $st) i32) (result i32)))
+    (type (;2;) (func (result i32)))
+    (import "" "spawn" (func (;0;) (type 1)))
+    (import "" "concurrency" (func (;1;) (type 2)))
+  )
+  (core instance (;0;)
+    (export "spawn" (func $spawn))
+    (export "concurrency" (func $concurrency))
+  )
+  (core instance (;1;) (instantiate $m
+      (with "" (instance 0))
+    )
+  )
+)