Repository: bytecodealliance/wasm-tools
Pull request #1626 (2024-06-20 13:58:25)

Initialize a test suite for `wit-encoder`
Follow-up to #1549 and #1580, this begins adding a test suite for `wit-encoder`. Specifically: this takes the tests from `wit-parser`, and then tries to encode those in individual tests. It fixes a number of bugs, as well as adds support for `include` statements. It also changes the default indentation for WIT documents to `2`, which matches the style that's been used in the WIT spec and WASI documents.

Not all `.wit` files have their own tests yet. But I ran out of time before I could finish up all the tests, but I figured even a partial test suite would already be a useful contribution. Thanks!

yoshuawuyts: lmao, [what in the line-endings](https://github.com/bytecodealliance/wasm-tools/actions/runs/9598811806/job/26471344027?pr=1626):

```text
---- concrete_types stdout ----
thread 'concrete_types' panicked at crates\wit-encoder\tests\empty.rs:9:5:
assertion failed: `(left == right)`

Diff < left / right > :
 package foo:empty;
```

Patch:
diff --git a/.gitattributes b/.gitattributes
index 684c5c1e..274b96ba 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,2 +1,3 @@
+* text=auto
 *.waves text eol=lf
 *.out text eol=lf
diff --git a/Cargo.lock b/Cargo.lock
index 5ed751ad..ee756535 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -801,6 +801,12 @@ dependencies = [
  "serde",
 ]
 
+[[package]]
+name = "indoc"
+version = "2.0.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
+
 [[package]]
 name = "instant"
 version = "0.1.12"
@@ -2205,6 +2211,7 @@ dependencies = [
 name = "wit-encoder"
 version = "0.211.1"
 dependencies = [
+ "indoc",
  "pretty_assertions",
  "semver",
 ]
diff --git a/Cargo.toml b/Cargo.toml
index a8b01554..d7d9e3d4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -84,6 +84,7 @@ bitflags = "2.5.0"
 hashbrown = { version = "0.14.3", default-features = false, features = ['ahash'] }
 ahash = { version = "0.8.11", default-features = false }
 termcolor = "1.2.0"
+indoc = "2.0.5"
 
 wasm-compose = { version = "0.211.1", path = "crates/wasm-compose" }
 wasm-encoder = { version = "0.211.1", path = "crates/wasm-encoder" }
diff --git a/crates/wasm-encoder/Cargo.toml b/crates/wasm-encoder/Cargo.toml
index 6a7478df..7472bd7b 100644
--- a/crates/wasm-encoder/Cargo.toml
+++ b/crates/wasm-encoder/Cargo.toml
@@ -13,6 +13,9 @@ A low-level WebAssembly encoder.
 """
 rust-version.workspace = true
 
+[package.metadata.docs.rs]
+all-features = true
+
 [lints]
 workspace = true
 
diff --git a/crates/wasm-encoder/src/core/code.rs b/crates/wasm-encoder/src/core/code.rs
index e8887fb2..25de73be 100644
--- a/crates/wasm-encoder/src/core/code.rs
+++ b/crates/wasm-encoder/src/core/code.rs
@@ -103,6 +103,30 @@ impl CodeSection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the code to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::CodeSectionReader<'_>,
+    ) -> wasmparser::Result<&mut Self> {
+        for code in section {
+            self.parse(code?)?;
+        }
+        Ok(self)
+    }
+
+    /// Parses a single [`wasmparser::Code`] and adds it to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse(&mut self, func: wasmparser::FunctionBody<'_>) -> wasmparser::Result<&mut Self> {
+        let mut f = Function::new_parsed_locals(&func)?;
+        let mut reader = func.get_operators_reader()?;
+        while !reader.eof() {
+            f.parse(&mut reader)?;
+        }
+        Ok(self.function(&f))
+    }
 }
 
 impl Encode for CodeSection {
@@ -214,6 +238,18 @@ impl Function {
         Function::new(locals_collected)
     }
 
+    /// Create a new [`Function`] by parsing the locals declarations from the
+    /// provided [`wasmparser::FunctionBody`].
+    #[cfg(feature = "wasmparser")]
+    pub fn new_parsed_locals(func: &wasmparser::FunctionBody<'_>) -> wasmparser::Result<Self> {
+        let mut locals = Vec::new();
+        for pair in func.get_locals_reader()? {
+            let (cnt, ty) = pair?;
+            locals.push((cnt, ty.try_into().unwrap()));
+        }
+        Ok(Function::new(locals))
+    }
+
     /// Write an instruction into this function body.
     pub fn instruction(&mut self, instruction: &Instruction) -> &mut Self {
         instruction.encode(&mut self.bytes);
@@ -237,6 +273,15 @@ impl Function {
     pub fn byte_len(&self) -> usize {
         self.bytes.len()
     }
+
+    /// Parses a single instruction from `reader` and adds it to `self`.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse(
+        &mut self,
+        reader: &mut wasmparser::OperatorsReader<'_>,
+    ) -> wasmparser::Result<&mut Self> {
+        Ok(self.instruction(&reader.read()?.try_into().unwrap()))
+    }
 }
 
 impl Encode for Function {
@@ -276,6 +321,17 @@ impl Encode for MemArg {
     }
 }
 
+#[cfg(feature = "wasmparser")]
+impl From<wasmparser::MemArg> for MemArg {
+    fn from(arg: wasmparser::MemArg) -> MemArg {
+        MemArg {
+            offset: arg.offset,
+            align: arg.align.into(),
+            memory_index: arg.memory,
+        }
+    }
+}
+
 /// The memory ordering for atomic instructions.
 ///
 /// For an in-depth explanation of memory orderings, see the C++ documentation
@@ -304,6 +360,16 @@ impl Encode for Ordering {
     }
 }
 
+#[cfg(feature = "wasmparser")]
+impl From<wasmparser::Ordering> for Ordering {
+    fn from(arg: wasmparser::Ordering) -> Ordering {
+        match arg {
+            wasmparser::Ordering::SeqCst => Ordering::SeqCst,
+            wasmparser::Ordering::AcqRel => Ordering::AcqRel,
+        }
+    }
+}
+
 /// Describe an unchecked SIMD lane index.
 pub type Lane = u8;
 
@@ -328,6 +394,18 @@ impl Encode for BlockType {
     }
 }
 
+#[cfg(feature = "wasmparser")]
+impl TryFrom<wasmparser::BlockType> for BlockType {
+    type Error = ();
+    fn try_from(arg: wasmparser::BlockType) -> Result<BlockType, ()> {
+        match arg {
+            wasmparser::BlockType::Empty => Ok(BlockType::Empty),
+            wasmparser::BlockType::FuncType(n) => Ok(BlockType::FunctionType(n)),
+            wasmparser::BlockType::Type(t) => Ok(BlockType::Result(t.try_into()?)),
+        }
+    }
+}
+
 /// WebAssembly instructions.
 #[derive(Clone, Debug)]
 #[non_exhaustive]
@@ -350,14 +428,14 @@ pub enum Instruction<'a> {
     Call(u32),
     CallRef(u32),
     CallIndirect {
-        ty: u32,
-        table: u32,
+        type_index: u32,
+        table_index: u32,
     },
     ReturnCallRef(u32),
     ReturnCall(u32),
     ReturnCallIndirect {
-        ty: u32,
-        table: u32,
+        type_index: u32,
+        table_index: u32,
     },
     TryTable(BlockType, Cow<'a, [Catch]>),
     Throw(u32),
@@ -1119,10 +1197,13 @@ impl Encode for Instruction<'_> {
                 sink.push(0x14);
                 ty.encode(sink);
             }
-            Instruction::CallIndirect { ty, table } => {
+            Instruction::CallIndirect {
+                type_index,
+                table_index,
+            } => {
                 sink.push(0x11);
-                ty.encode(sink);
-                table.encode(sink);
+                type_index.encode(sink);
+                table_index.encode(sink);
             }
             Instruction::ReturnCallRef(ty) => {
                 sink.push(0x15);
@@ -1133,10 +1214,13 @@ impl Encode for Instruction<'_> {
                 sink.push(0x12);
                 f.encode(sink);
             }
-            Instruction::ReturnCallIndirect { ty, table } => {
+            Instruction::ReturnCallIndirect {
+                type_index,
+                table_index,
+            } => {
                 sink.push(0x13);
-                ty.encode(sink);
-                table.encode(sink);
+                type_index.encode(sink);
+                table_index.encode(sink);
             }
             Instruction::Delegate(l) => {
                 sink.push(0x18);
@@ -3278,6 +3362,64 @@ impl Encode for Instruction<'_> {
     }
 }
 
+#[cfg(feature = "wasmparser")]
+impl TryFrom<wasmparser::Operator<'_>> for Instruction<'_> {
+    type Error = ();
+
+    fn try_from(arg: wasmparser::Operator<'_>) -> Result<Self, ()> {
+        use Instruction::*;
+
+        macro_rules! define_match {
+            ($(@$p:ident $op:ident $({ $($arg:ident: $argty:ty),* })? => $visit:ident)*) => {
+                match arg {
+                    $(
+                        wasmparser::Operator::$op $({ $($arg),* })? => {
+                            $(
+                                $(let $arg = define_match!(map $arg $arg);)*
+                            )?
+                            Ok(define_match!(mk $op $($($arg)*)?))
+                        }
+                    )*
+                }
+            };
+
+            // No-payload instructions are named the same in wasmparser as they are in
+            // wasm-encoder
+            (mk $op:ident) => ($op);
+
+            // Instructions which need "special care" to map from wasmparser to
+            // wasm-encoder
+            (mk BrTable $arg:ident) => ({BrTable($arg.0, $arg.1)});
+            (mk TryTable $arg:ident) => ({TryTable($arg.0, $arg.1)});
+
+            // Catch-all for the translation of one payload argument which is typically
+            // represented as a tuple-enum in wasm-encoder.
+            (mk $op:ident $arg:ident) => ($op($arg));
+
+            // Catch-all of everything else where the wasmparser fields are simply
+            // translated to wasm-encoder fields.
+            (mk $op:ident $($arg:ident)*) => ($op { $($arg),* });
+
+            // Special-case BrTable/TryTable conversion of arguments.
+            (map $arg:ident targets) => ((
+                $arg.targets().map(|i| i.unwrap()).collect::<Vec<_>>().into(),
+                $arg.default(),
+            ));
+            (map $arg:ident try_table) => ((
+                $arg.ty.try_into().unwrap(),
+                $arg.catches.into_iter().map(|i| i.into()).collect::<Vec<_>>().into(),
+            ));
+
+            // Everything else is converted with `TryFrom`/`From`. Note that the
+            // fallibility here has to do with how indexes are represented in
+            // `wasmparser` which we know when reading directly we'll never hit the
+            // erroneous cases here, hence the unwrap.
+            (map $arg:ident $other:ident) => {$other.try_into().unwrap()};
+        }
+        wasmparser::for_each_operator!(define_match)
+    }
+}
+
 #[derive(Clone, Debug)]
 #[allow(missing_docs)]
 pub enum Catch {
@@ -3312,6 +3454,18 @@ impl Encode for Catch {
     }
 }
 
+#[cfg(feature = "wasmparser")]
+impl From<wasmparser::Catch> for Catch {
+    fn from(arg: wasmparser::Catch) -> Catch {
+        match arg {
+            wasmparser::Catch::One { tag, label } => Catch::One { tag, label },
+            wasmparser::Catch::OneRef { tag, label } => Catch::OneRef { tag, label },
+            wasmparser::Catch::All { label } => Catch::All { label },
+            wasmparser::Catch::AllRef { label } => Catch::AllRef { label },
+        }
+    }
+}
+
 /// A constant expression.
 ///
 /// Usable in contexts such as offsets or initializers.
@@ -3494,6 +3648,13 @@ pub enum ConstExprConversionError {
     CanonicalizedTypeReference,
 }
 
+#[cfg(feature = "wasmparser")]
+impl From<wasmparser::BinaryReaderError> for ConstExprConversionError {
+    fn from(err: wasmparser::BinaryReaderError) -> ConstExprConversionError {
+        ConstExprConversionError::ParseError(err)
+    }
+}
+
 #[cfg(feature = "wasmparser")]
 impl std::fmt::Display for ConstExprConversionError {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
diff --git a/crates/wasm-encoder/src/core/custom.rs b/crates/wasm-encoder/src/core/custom.rs
index 870ee626..75cae490 100644
--- a/crates/wasm-encoder/src/core/custom.rs
+++ b/crates/wasm-encoder/src/core/custom.rs
@@ -44,6 +44,16 @@ impl Section for RawCustomSection<'_> {
     }
 }
 
+#[cfg(feature = "wasmparser")]
+impl<'a> From<wasmparser::CustomSectionReader<'a>> for CustomSection<'a> {
+    fn from(section: wasmparser::CustomSectionReader<'a>) -> Self {
+        CustomSection {
+            data: section.data().into(),
+            name: section.name().into(),
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use super::*;
diff --git a/crates/wasm-encoder/src/core/data.rs b/crates/wasm-encoder/src/core/data.rs
index 6f408bef..7f8dd795 100644
--- a/crates/wasm-encoder/src/core/data.rs
+++ b/crates/wasm-encoder/src/core/data.rs
@@ -151,6 +151,38 @@ impl DataSection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the data to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::DataSectionReader<'_>,
+    ) -> Result<&mut Self, crate::ConstExprConversionError> {
+        for data in section {
+            self.parse(data?)?;
+        }
+        Ok(self)
+    }
+
+    /// Parses a single [`wasmparser::Data`] and adds it to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse(
+        &mut self,
+        data: wasmparser::Data<'_>,
+    ) -> Result<&mut Self, crate::ConstExprConversionError> {
+        match data.kind {
+            wasmparser::DataKind::Active {
+                memory_index,
+                offset_expr,
+            } => Ok(self.active(
+                memory_index,
+                &ConstExpr::try_from(offset_expr)?,
+                data.data.iter().copied(),
+            )),
+            wasmparser::DataKind::Passive => Ok(self.passive(data.data.iter().copied())),
+        }
+    }
 }
 
 impl Encode for DataSection {
diff --git a/crates/wasm-encoder/src/core/elements.rs b/crates/wasm-encoder/src/core/elements.rs
index 28a1031e..8d24c4e3 100644
--- a/crates/wasm-encoder/src/core/elements.rs
+++ b/crates/wasm-encoder/src/core/elements.rs
@@ -206,6 +206,54 @@ impl ElementSection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the elements to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::ElementSectionReader<'_>,
+    ) -> Result<&mut Self, crate::ConstExprConversionError> {
+        for element in section {
+            self.parse(element?)?;
+        }
+        Ok(self)
+    }
+
+    /// Parses the single [`wasmparser::Element`] provided and adds it to this
+    /// section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse(
+        &mut self,
+        element: wasmparser::Element<'_>,
+    ) -> Result<&mut Self, crate::ConstExprConversionError> {
+        let mut funcs;
+        let mut exprs;
+        let elements = match element.items {
+            wasmparser::ElementItems::Functions(f) => {
+                funcs = Vec::new();
+                for func in f {
+                    funcs.push(func?);
+                }
+                Elements::Functions(&funcs)
+            }
+            wasmparser::ElementItems::Expressions(ty, e) => {
+                exprs = Vec::new();
+                for expr in e {
+                    exprs.push(ConstExpr::try_from(expr?)?);
+                }
+                Elements::Expressions(ty.try_into().unwrap(), &exprs)
+            }
+        };
+        match element.kind {
+            wasmparser::ElementKind::Active {
+                table_index,
+                offset_expr,
+            } => Ok(self.active(table_index, &ConstExpr::try_from(offset_expr)?, elements)),
+            wasmparser::ElementKind::Passive => Ok(self.passive(elements)),
+            wasmparser::ElementKind::Declared => Ok(self.declared(elements)),
+        }
+    }
 }
 
 impl Encode for ElementSection {
diff --git a/crates/wasm-encoder/src/core/exports.rs b/crates/wasm-encoder/src/core/exports.rs
index fc2dc9c3..7b06fabc 100644
--- a/crates/wasm-encoder/src/core/exports.rs
+++ b/crates/wasm-encoder/src/core/exports.rs
@@ -83,6 +83,26 @@ impl ExportSection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the exports to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::ExportSectionReader<'_>,
+    ) -> wasmparser::Result<&mut Self> {
+        for export in section {
+            self.parse(export?);
+        }
+        Ok(self)
+    }
+
+    /// Parses the single [`wasmparser::Export`] provided and adds it to this
+    /// section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse(&mut self, export: wasmparser::Export<'_>) -> &mut Self {
+        self.export(export.name, export.kind.into(), export.index)
+    }
 }
 
 impl Encode for ExportSection {
diff --git a/crates/wasm-encoder/src/core/functions.rs b/crates/wasm-encoder/src/core/functions.rs
index e21d8c10..3cc053ba 100644
--- a/crates/wasm-encoder/src/core/functions.rs
+++ b/crates/wasm-encoder/src/core/functions.rs
@@ -48,6 +48,19 @@ impl FunctionSection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the functions to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::FunctionSectionReader<'_>,
+    ) -> wasmparser::Result<&mut Self> {
+        for func in section {
+            self.function(func?);
+        }
+        Ok(self)
+    }
 }
 
 impl Encode for FunctionSection {
diff --git a/crates/wasm-encoder/src/core/globals.rs b/crates/wasm-encoder/src/core/globals.rs
index 291ca804..f208f63f 100644
--- a/crates/wasm-encoder/src/core/globals.rs
+++ b/crates/wasm-encoder/src/core/globals.rs
@@ -60,6 +60,30 @@ impl GlobalSection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the globals to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::GlobalSectionReader<'_>,
+    ) -> Result<&mut Self, crate::ConstExprConversionError> {
+        for global in section {
+            self.parse(global?)?;
+        }
+        Ok(self)
+    }
+
+    /// Parses the single [`wasmparser::Global`] provided and adds it to this
+    /// section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse(
+        &mut self,
+        global: wasmparser::Global<'_>,
+    ) -> Result<&mut Self, crate::ConstExprConversionError> {
+        self.global(global.ty.try_into().unwrap(), &global.init_expr.try_into()?);
+        Ok(self)
+    }
 }
 
 impl Encode for GlobalSection {
diff --git a/crates/wasm-encoder/src/core/imports.rs b/crates/wasm-encoder/src/core/imports.rs
index d4db2e65..5eac839c 100644
--- a/crates/wasm-encoder/src/core/imports.rs
+++ b/crates/wasm-encoder/src/core/imports.rs
@@ -142,6 +142,31 @@ impl ImportSection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the imports to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::ImportSectionReader<'_>,
+    ) -> wasmparser::Result<&mut Self> {
+        for import in section {
+            self.parse(import?);
+        }
+        Ok(self)
+    }
+
+    /// Parses the single [`wasmparser::Import`] provided and adds it to this
+    /// section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse(&mut self, import: wasmparser::Import<'_>) -> &mut Self {
+        self.import(
+            import.module,
+            import.name,
+            EntityType::try_from(import.ty).unwrap(),
+        );
+        self
+    }
 }
 
 impl Encode for ImportSection {
diff --git a/crates/wasm-encoder/src/core/memories.rs b/crates/wasm-encoder/src/core/memories.rs
index d64ef012..b3942dfa 100644
--- a/crates/wasm-encoder/src/core/memories.rs
+++ b/crates/wasm-encoder/src/core/memories.rs
@@ -51,6 +51,20 @@ impl MemorySection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the memories to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::MemorySectionReader<'_>,
+    ) -> wasmparser::Result<&mut Self> {
+        for memory in section {
+            let memory = memory?;
+            self.memory(memory.into());
+        }
+        Ok(self)
+    }
 }
 
 impl Encode for MemorySection {
diff --git a/crates/wasm-encoder/src/core/tables.rs b/crates/wasm-encoder/src/core/tables.rs
index 7467f5f7..24bfe03a 100644
--- a/crates/wasm-encoder/src/core/tables.rs
+++ b/crates/wasm-encoder/src/core/tables.rs
@@ -62,6 +62,37 @@ impl TableSection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the tables to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::TableSectionReader<'_>,
+    ) -> Result<&mut Self, crate::ConstExprConversionError> {
+        for table in section {
+            self.parse(table?)?;
+        }
+        Ok(self)
+    }
+
+    /// Parses a single [`wasmparser::Table`] and adds it to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse(
+        &mut self,
+        table: wasmparser::Table<'_>,
+    ) -> Result<&mut Self, crate::ConstExprConversionError> {
+        let ty = table.ty.try_into().unwrap();
+        match table.init {
+            wasmparser::TableInit::RefNull => {
+                self.table(ty);
+            }
+            wasmparser::TableInit::Expr(e) => {
+                self.table_with_init(ty, &e.try_into()?);
+            }
+        }
+        Ok(self)
+    }
 }
 
 impl Encode for TableSection {
diff --git a/crates/wasm-encoder/src/core/tags.rs b/crates/wasm-encoder/src/core/tags.rs
index 9b4c728a..2d6a88d9 100644
--- a/crates/wasm-encoder/src/core/tags.rs
+++ b/crates/wasm-encoder/src/core/tags.rs
@@ -46,6 +46,20 @@ impl TagSection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the tags to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::TagSectionReader<'_>,
+    ) -> wasmparser::Result<&mut Self> {
+        for tag in section {
+            let tag = tag?;
+            self.tag(tag.into());
+        }
+        Ok(self)
+    }
 }
 
 impl Encode for TagSection {
diff --git a/crates/wasm-encoder/src/core/types.rs b/crates/wasm-encoder/src/core/types.rs
index 0fa12f68..0f53326b 100644
--- a/crates/wasm-encoder/src/core/types.rs
+++ b/crates/wasm-encoder/src/core/types.rs
@@ -757,6 +757,31 @@ impl TypeSection {
         self.num_added += 1;
         self
     }
+
+    /// Parses the input `section` given from the `wasmparser` crate and adds
+    /// all the types to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse_section(
+        &mut self,
+        section: wasmparser::TypeSectionReader<'_>,
+    ) -> wasmparser::Result<&mut Self> {
+        for rec_group in section {
+            self.parse(rec_group?);
+        }
+        Ok(self)
+    }
+
+    /// Parses a single [`wasmparser::RecGroup`] and adds it to this section.
+    #[cfg(feature = "wasmparser")]
+    pub fn parse(&mut self, rec_group: wasmparser::RecGroup) -> &mut Self {
+        if rec_group.is_explicit_rec_group() {
+            self.rec(rec_group.into_types().map(|t| t.try_into().unwrap()));
+        } else {
+            let ty = rec_group.into_types().next().unwrap();
+            self.subtype(&SubType::try_from(ty).unwrap());
+        }
+        self
+    }
 }
 
 impl Encode for TypeSection {
diff --git a/crates/wasm-encoder/src/lib.rs b/crates/wasm-encoder/src/lib.rs
index 930c63ec..ae30a643 100644
--- a/crates/wasm-encoder/src/lib.rs
+++ b/crates/wasm-encoder/src/lib.rs
@@ -68,6 +68,7 @@
 //! assert!(wasmparser::validate(&wasm_bytes).is_ok());
 //! ```
 
+#![cfg_attr(docsrs, feature(doc_auto_cfg))]
 #![deny(missing_docs, missing_debug_implementations)]
 
 mod component;
diff --git a/crates/wasm-mutate/src/mutators/translate.rs b/crates/wasm-mutate/src/mutators/translate.rs
index 839879bc..706e7629 100644
--- a/crates/wasm-mutate/src/mutators/translate.rs
+++ b/crates/wasm-mutate/src/mutators/translate.rs
@@ -399,14 +399,6 @@ pub fn op(t: &mut dyn Translator, op: &Operator<'_>) -> Result<Instruction<'stat
         (build V128Const $arg:ident) => (I::V128Const($arg.i128()));
         (build TryTable $table:ident) => (unimplemented_try_table());
         (build $op:ident $arg:ident) => (I::$op($arg));
-        (build CallIndirect $ty:ident $table:ident) => (I::CallIndirect {
-            ty: $ty,
-            table: $table,
-        });
-        (build ReturnCallIndirect $ty:ident $table:ident) => (I::ReturnCallIndirect {
-            ty: $ty,
-            table: $table,
-        });
         (build $op:ident $($arg:ident)*) => (I::$op { $($arg),* });
     }
 
diff --git a/crates/wasm-smith/src/core.rs b/crates/wasm-smith/src/core.rs
index 29514f7a..096a1ef7 100644
--- a/crates/wasm-smith/src/core.rs
+++ b/crates/wasm-smith/src/core.rs
@@ -1673,59 +1673,6 @@ impl Module {
 
     #[cfg(feature = "wasmparser")]
     fn _required_exports(&mut self, u: &mut Unstructured, example_module: &[u8]) -> Result<()> {
-        fn convert_heap_type(ty: &wasmparser::HeapType) -> HeapType {
-            use wasmparser::AbstractHeapType::*;
-            match ty {
-                wasmparser::HeapType::Concrete(_) => {
-                    panic!("Unable to handle concrete types in exports")
-                }
-                wasmparser::HeapType::Abstract { shared, ty } => {
-                    let ty = match ty {
-                        Func => AbstractHeapType::Func,
-                        Extern => AbstractHeapType::Extern,
-                        Any => AbstractHeapType::Any,
-                        None => AbstractHeapType::None,
-                        NoExtern => AbstractHeapType::NoExtern,
-                        NoFunc => AbstractHeapType::NoFunc,
-                        Eq => AbstractHeapType::Eq,
-                        Struct => AbstractHeapType::Struct,
-                        Array => AbstractHeapType::Array,
-                        I31 => AbstractHeapType::I31,
-                        Exn => AbstractHeapType::Exn,
-                        NoExn => AbstractHeapType::NoExn,
-                    };
-                    HeapType::Abstract {
-                        shared: *shared,
-                        ty,
-                    }
-                }
-            }
-        }
-
-        fn convert_val_type(ty: &wasmparser::ValType) -> ValType {
-            match ty {
-                wasmparser::ValType::I32 => ValType::I32,
-                wasmparser::ValType::I64 => ValType::I64,
-                wasmparser::ValType::F32 => ValType::F32,
-                wasmparser::ValType::F64 => ValType::F64,
-                wasmparser::ValType::V128 => ValType::V128,
-                wasmparser::ValType::Ref(r) => ValType::Ref(RefType {
-                    nullable: r.is_nullable(),
-                    heap_type: convert_heap_type(&r.heap_type()),
-                }),
-            }
-        }
-
-        fn convert_export_kind(kind: &wasmparser::ExternalKind) -> ExportKind {
-            match kind {
-                wasmparser::ExternalKind::Func => ExportKind::Func,
-                wasmparser::ExternalKind::Table => ExportKind::Table,
-                wasmparser::ExternalKind::Memory => ExportKind::Memory,
-                wasmparser::ExternalKind::Global => ExportKind::Global,
-                wasmparser::ExternalKind::Tag => ExportKind::Tag,
-            }
-        }
-
         let mut required_exports: Vec<wasmparser::Export> = vec![];
         let mut validator = wasmparser::Validator::new();
         let exports_types = validator
@@ -1776,13 +1723,15 @@ impl Module {
                             let new_type = Rc::new(FuncType {
                                 params: func_type
                                     .params()
-                                    .into_iter()
-                                    .map(convert_val_type)
+                                    .iter()
+                                    .copied()
+                                    .map(|t| t.try_into().unwrap())
                                     .collect(),
                                 results: func_type
                                     .results()
-                                    .into_iter()
-                                    .map(convert_val_type)
+                                    .iter()
+                                    .copied()
+                                    .map(|t| t.try_into().unwrap())
                                     .collect(),
                             });
                             self.rec_groups.push(self.types.len()..self.types.len() + 1);
@@ -1803,15 +1752,9 @@ impl Module {
                     }
                 }
                 // For globals, add a new global.
-                wasmparser::types::EntityType::Global(global_type) => self
-                    .add_arbitrary_global_of_type(
-                        GlobalType {
-                            val_type: convert_val_type(&global_type.content_type),
-                            mutable: global_type.mutable,
-                            shared: global_type.shared,
-                        },
-                        u,
-                    )?,
+                wasmparser::types::EntityType::Global(global_type) => {
+                    self.add_arbitrary_global_of_type(global_type.try_into().unwrap(), u)?
+                }
                 wasmparser::types::EntityType::Table(_)
                 | wasmparser::types::EntityType::Memory(_)
                 | wasmparser::types::EntityType::Tag(_) => {
@@ -1821,11 +1764,8 @@ impl Module {
                     )
                 }
             };
-            self.exports.push((
-                export.name.to_string(),
-                convert_export_kind(&export.kind),
-                new_index,
-            ));
+            self.exports
+                .push((export.name.to_string(), export.kind.into(), new_index));
             self.export_names.insert(export.name.to_string());
         }
 
diff --git a/crates/wasm-smith/src/core/code_builder.rs b/crates/wasm-smith/src/core/code_builder.rs
index d93ce0c0..97fdaa36 100644
--- a/crates/wasm-smith/src/core/code_builder.rs
+++ b/crates/wasm-smith/src/core/code_builder.rs
@@ -2091,8 +2091,8 @@ fn call_indirect(
     builder.pop_operands(module, &ty.params);
     builder.push_operands(&ty.results);
     instructions.push(Instruction::CallIndirect {
-        ty: *type_idx as u32,
-        table,
+        type_index: *type_idx as u32,
+        table_index: table,
     });
     Ok(())
 }
@@ -2228,8 +2228,8 @@ fn return_call_indirect(
     builder.pop_operands(module, &ty.params);
     builder.push_operands(&ty.results);
     instructions.push(Instruction::ReturnCallIndirect {
-        ty: *type_idx as u32,
-        table,
+        type_index: *type_idx as u32,
+        table_index: table,
     });
     Ok(())
 }
diff --git a/crates/wasmparser/Cargo.toml b/crates/wasmparser/Cargo.toml
index d65cd467..117681d7 100644
--- a/crates/wasmparser/Cargo.toml
+++ b/crates/wasmparser/Cargo.toml
@@ -16,6 +16,9 @@ rust-version.workspace = true
 [lints]
 workspace = true
 
+[package.metadata.docs.rs]
+all-features = true
+
 [dependencies]
 bitflags = "2.4.1"
 indexmap = { workspace = true, optional = true }
diff --git a/crates/wasmparser/src/readers/core/operators.rs b/crates/wasmparser/src/readers/core/operators.rs
index 69f8baec..ff3ef843 100644
--- a/crates/wasmparser/src/readers/core/operators.rs
+++ b/crates/wasmparser/src/readers/core/operators.rs
@@ -78,6 +78,12 @@ impl Ieee32 {
     }
 }
 
+impl From<Ieee32> for f32 {
+    fn from(bits: Ieee32) -> f32 {
+        f32::from_bits(bits.bits())
+    }
+}
+
 /// An IEEE binary64 immediate floating point value, represented as a u64
 /// containing the bit pattern.
 ///
@@ -92,6 +98,12 @@ impl Ieee64 {
     }
 }
 
+impl From<Ieee64> for f64 {
+    fn from(bits: Ieee64) -> f64 {
+        f64::from_bits(bits.bits())
+    }
+}
+
 /// Represents a 128-bit vector value.
 #[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
 pub struct V128(pub(crate) [u8; 16]);
@@ -108,6 +120,18 @@ impl V128 {
     }
 }
 
+impl From<V128> for i128 {
+    fn from(bits: V128) -> i128 {
+        bits.i128()
+    }
+}
+
+impl From<V128> for u128 {
+    fn from(bits: V128) -> u128 {
+        u128::from_le_bytes(bits.0)
+    }
+}
+
 /// Represents the memory ordering for atomic instructions.
 ///
 /// For an in-depth explanation of memory orderings, see the C++ documentation
diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs
index 56eecf06..f045cb83 100644
--- a/crates/wit-component/src/encoding.rs
+++ b/crates/wit-component/src/encoding.rs
@@ -1334,8 +1334,8 @@ impl<'a> EncodingState<'a> {
         }
         func.instruction(&Instruction::I32Const(func_index as i32));
         func.instruction(&Instruction::CallIndirect {
-            ty: type_index,
-            table: 0,
+            type_index,
+            table_index: 0,
         });
         func.instruction(&Instruction::End);
         code.function(&func);
diff --git a/crates/wit-component/src/gc.rs b/crates/wit-component/src/gc.rs
index bb857f94..0fa13d85 100644
--- a/crates/wit-component/src/gc.rs
+++ b/crates/wit-component/src/gc.rs
@@ -568,13 +568,7 @@ impl<'a> Module<'a> {
         let mut num_memories = 0;
         for (i, mem) in self.live_memories() {
             map.memories.push(i);
-            let ty = wasm_encoder::MemoryType {
-                minimum: mem.ty.initial,
-                maximum: mem.ty.maximum,
-                shared: mem.ty.shared,
-                memory64: mem.ty.memory64,
-                page_size_log2: mem.ty.page_size_log2,
-            };
+            let ty = mem.ty.into();
             match &mem.def {
                 Definition::Import(m, n) => {
                     imports.import(m, n, ty);
@@ -1165,12 +1159,6 @@ macro_rules! define_encode {
     (mk BrTable $arg:ident) => ({
         BrTable($arg.0, $arg.1)
     });
-    (mk CallIndirect $ty:ident $table:ident) => ({
-        CallIndirect { ty: $ty, table: $table }
-    });
-    (mk ReturnCallIndirect $ty:ident $table:ident) => (
-        ReturnCallIndirect { ty: $ty, table: $table }
-    );
     (mk TryTable $try_table:ident) => ({
         let _ = $try_table;
         unimplemented_try_table()
diff --git a/crates/wit-component/src/linking.rs b/crates/wit-component/src/linking.rs
index e3923c1e..855fc0f3 100644
--- a/crates/wit-component/src/linking.rs
+++ b/crates/wit-component/src/linking.rs
@@ -434,8 +434,8 @@ fn make_env_module<'a>(
         }
         function.instruction(&Ins::I32Const(i32::try_from(table_offset).unwrap()));
         function.instruction(&Ins::CallIndirect {
-            ty: u32::try_from(index).unwrap(),
-            table: 0,
+            type_index: u32::try_from(index).unwrap(),
+            table_index: 0,
         });
         function.instruction(&Ins::End);
         code.function(&function);
diff --git a/crates/wit-encoder/Cargo.toml b/crates/wit-encoder/Cargo.toml
index ae5da013..00f80ba3 100644
--- a/crates/wit-encoder/Cargo.toml
+++ b/crates/wit-encoder/Cargo.toml
@@ -13,3 +13,6 @@ workspace = true
 [dependencies]
 semver = { workspace = true }
 pretty_assertions = { workspace = true }
+
+[dev-dependencies]
+indoc = { workspace = true }
diff --git a/crates/wit-encoder/src/function.rs b/crates/wit-encoder/src/function.rs
index fa3e81e2..e0ffe79e 100644
--- a/crates/wit-encoder/src/function.rs
+++ b/crates/wit-encoder/src/function.rs
@@ -108,7 +108,7 @@ where
 impl Results {
     // For the common case of an empty results list.
     pub fn empty() -> Results {
-        Results::Named(Default::default())
+        Results::Named(Params::empty())
     }
 
     pub fn anon(type_: Type) -> Results {
@@ -166,3 +166,14 @@ impl StandaloneFunc {
         self.docs = docs.map(|d| d.into());
     }
 }
+
+#[cfg(test)]
+mod test {
+    use crate::Results;
+
+    #[test]
+    fn is_empty() {
+        let res = Results::empty();
+        assert!(res.is_empty());
+    }
+}
diff --git a/crates/wit-encoder/src/include.rs b/crates/wit-encoder/src/include.rs
new file mode 100644
index 00000000..747186cc
--- /dev/null
+++ b/crates/wit-encoder/src/include.rs
@@ -0,0 +1,26 @@
+use std::fmt;
+
+use crate::{Ident, Render};
+
+/// Enable the union of a world with another world
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Include {
+    use_path: Ident,
+    include_names_list: Vec<String>,
+}
+
+impl Include {
+    pub fn new(use_path: impl Into<Ident>) -> Self {
+        Self {
+            use_path: use_path.into(),
+            include_names_list: vec![],
+        }
+    }
+}
+
+impl Render for Include {
+    fn render(&self, f: &mut fmt::Formatter<'_>, opts: &crate::RenderOpts) -> fmt::Result {
+        write!(f, "{}include {};\n", opts.spaces(), self.use_path)?;
+        Ok(())
+    }
+}
diff --git a/crates/wit-encoder/src/lib.rs b/crates/wit-encoder/src/lib.rs
index 908904da..c4e8eb4f 100644
--- a/crates/wit-encoder/src/lib.rs
+++ b/crates/wit-encoder/src/lib.rs
@@ -8,6 +8,7 @@ mod enum_;
 mod flags;
 mod function;
 mod ident;
+mod include;
 mod interface;
 mod package;
 mod record;
@@ -24,6 +25,7 @@ pub use enum_::*;
 pub use flags::*;
 pub use function::*;
 pub use ident::*;
+pub use include::*;
 pub use interface::*;
 pub use package::*;
 pub use record::*;
diff --git a/crates/wit-encoder/src/package.rs b/crates/wit-encoder/src/package.rs
index d25abfc4..2c5eca4c 100644
--- a/crates/wit-encoder/src/package.rs
+++ b/crates/wit-encoder/src/package.rs
@@ -41,16 +41,21 @@ impl Package {
 impl Render for Package {
     fn render(&self, f: &mut fmt::Formatter<'_>, opts: &RenderOpts) -> fmt::Result {
         write!(f, "{}package {};\n", opts.spaces(), self.name)?;
-        write!(f, "\n")?;
         for item in &self.items {
+            write!(f, "\n")?;
             match item {
                 PackageItem::Interface(interface) => {
                     if let Some(docs) = &interface.docs {
                         docs.render(f, opts)?;
                     }
-                    write!(f, "{}interface {} {{\n", opts.spaces(), interface.name)?;
-                    interface.items.render(f, &opts.indent())?;
-                    write!(f, "{}}}\n", opts.spaces())?;
+                    write!(f, "{}interface {} {{", opts.spaces(), interface.name)?;
+                    if !interface.items.is_empty() {
+                        write!(f, "\n")?;
+                        interface.items.render(f, &opts.indent())?;
+                        write!(f, "{}}}\n", opts.spaces())?;
+                    } else {
+                        write!(f, "}}\n")?;
+                    }
                 }
                 PackageItem::World(world) => {
                     world.render(f, opts)?;
diff --git a/crates/wit-encoder/src/render.rs b/crates/wit-encoder/src/render.rs
index 2339e320..62cda3c8 100644
--- a/crates/wit-encoder/src/render.rs
+++ b/crates/wit-encoder/src/render.rs
@@ -11,7 +11,7 @@ pub struct RenderOpts {
 impl Default for RenderOpts {
     fn default() -> Self {
         Self {
-            indent_width: 4,
+            indent_width: 2,
             ident_count: 0,
         }
     }
diff --git a/crates/wit-encoder/src/ty.rs b/crates/wit-encoder/src/ty.rs
index dea714e4..ec2e0a08 100644
--- a/crates/wit-encoder/src/ty.rs
+++ b/crates/wit-encoder/src/ty.rs
@@ -317,8 +317,11 @@ impl Render for TypeDef {
                 if let Some(docs) = &self.docs {
                     docs.render(f, opts)?;
                 }
-                write!(f, "{}record {} {{\n", opts.spaces(), self.name)?;
-                for field in &record.fields {
+                write!(f, "{}record {} {{", opts.spaces(), self.name)?;
+                for (index, field) in record.fields.iter().enumerate() {
+                    if index == 0 {
+                        write!(f, "\n")?;
+                    }
                     let opts = opts.indent();
                     if let Some(docs) = &field.docs {
                         docs.render(f, &opts)?;
diff --git a/crates/wit-encoder/src/world.rs b/crates/wit-encoder/src/world.rs
index 867d5505..5f837678 100644
--- a/crates/wit-encoder/src/world.rs
+++ b/crates/wit-encoder/src/world.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use crate::{ident::Ident, Docs, Interface, Render, RenderOpts, StandaloneFunc};
+use crate::{ident::Ident, Docs, Include, Interface, Render, RenderOpts, StandaloneFunc};
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct World {
@@ -52,6 +52,9 @@ impl World {
     pub fn function_export(&mut self, value: StandaloneFunc) {
         self.item(WorldItem::function_export(value));
     }
+    pub fn include(&mut self, value: impl Into<Ident>) {
+        self.item(WorldItem::include(value));
+    }
 
     /// Set the documentation
     pub fn docs(&mut self, docs: Option<impl Into<Docs>>) {
@@ -88,18 +91,28 @@ impl Render for World {
                         docs.render(f, opts)?;
                     }
                     import(f, opts)?;
-                    write!(f, "{}: interface {{\n", interface.name)?;
-                    interface.items.render(f, &opts.indent())?;
-                    write!(f, "{}}}\n", opts.spaces())?;
+                    write!(f, "{}: interface {{", interface.name)?;
+                    if !interface.items.is_empty() {
+                        write!(f, "\n")?;
+                        interface.items.render(f, &opts.indent())?;
+                        write!(f, "{}}}\n", opts.spaces())?;
+                    } else {
+                        write!(f, "}}\n")?;
+                    }
                 }
                 WorldItem::InlineInterfaceExport(interface) => {
                     if let Some(docs) = &interface.docs {
                         docs.render(f, opts)?;
                     }
                     export(f, opts)?;
-                    write!(f, "{}: interface {{\n", interface.name)?;
-                    interface.items.render(f, &opts.indent())?;
-                    write!(f, "{}}}\n", opts.spaces())?;
+                    write!(f, "{}: interface {{", interface.name)?;
+                    if !interface.items.is_empty() {
+                        write!(f, "\n")?;
+                        interface.items.render(f, &opts.indent())?;
+                        write!(f, "{}}}\n", opts.spaces())?;
+                    } else {
+                        write!(f, "}}\n")?;
+                    }
                 }
                 WorldItem::NamedInterfaceImport(interface) => {
                     if let Some(docs) = &interface.docs {
@@ -129,6 +142,7 @@ impl Render for World {
                     export(f, opts)?;
                     render_function(f, opts, function)?;
                 }
+                WorldItem::Include(include) => include.render(f, opts)?,
             }
         }
         let opts = &opts.outdent();
@@ -156,6 +170,9 @@ pub enum WorldItem {
 
     /// A function is being directly exported from this world.
     FunctionExport(StandaloneFunc),
+
+    /// Include type
+    Include(Include),
 }
 
 impl WorldItem {
@@ -177,6 +194,9 @@ impl WorldItem {
     pub fn function_export(value: StandaloneFunc) -> Self {
         Self::FunctionExport(value)
     }
+    pub fn include(value: impl Into<Ident>) -> Self {
+        Self::Include(Include::new(value))
+    }
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]


Test patch:
diff --git a/crates/wit-encoder/tests/empty.rs b/crates/wit-encoder/tests/empty.rs
new file mode 100644
index 00000000..ecf28aca
--- /dev/null
+++ b/crates/wit-encoder/tests/empty.rs
@@ -0,0 +1,12 @@
+use pretty_assertions::assert_eq;
+use wit_encoder::{Package, PackageName};
+
+const PACKAGE: &str = indoc::indoc! {"
+    package foo:empty;
+"};
+
+#[test]
+fn concrete_types() {
+    let package = Package::new(PackageName::new("foo", "empty", None));
+    assert_eq!(package.to_string(), PACKAGE);
+}
diff --git a/crates/wit-encoder/tests/functions.rs b/crates/wit-encoder/tests/functions.rs
index fd5c5e8c..b65a7b45 100644
--- a/crates/wit-encoder/tests/functions.rs
+++ b/crates/wit-encoder/tests/functions.rs
@@ -1,32 +1,33 @@
 use pretty_assertions::assert_eq;
-use wit_encoder::{Params, Result_, Results, StandaloneFunc, Type};
+use wit_encoder::{
+    Interface, Package, PackageName, Params, Result_, Results, StandaloneFunc, Type,
+};
 
-const PACKAGE: &str = "package foo:functions;
+const PACKAGE: &str = indoc::indoc! {"
+    package foo:functions;
 
-interface functions {
-    f1: func();
-    f2: func(a: u32);
-    f3: func() -> u32;
-    /// this is a documentation comment
-    /// for the f4 function
-    f4: func() -> tuple<u32, u32>;
-    f5: func(a: f32, b: f32) -> tuple<u32, u32>;
-    f6: func(a: option<u32>) -> result<u32, f32>;
-    f7: func() -> (u: u32, f: f32);
-    f8: func() -> (u: u32);
-    f9: func() -> result<f32>;
-    f10: func() -> result<_, f32>;
-    f11: func() -> result;
-}
-";
+    interface functions {
+      f1: func();
+      f2: func(a: u32);
+      f4: func() -> u32;
+      f6: func() -> tuple<u32, u32>;
+      f7: func(a: f32, b: f32) -> tuple<u32, u32>;
+      f8: func(a: option<u32>) -> result<u32, f32>;
+      f9: func() -> (u: u32, f: f32);
+      f10: func() -> (u: u32);
+      f11: func() -> result<f32>;
+      f12: func() -> result<_, f32>;
+      f13: func() -> result;
+    }
+"};
 
 #[test]
-fn smoke() {
-    let name = wit_encoder::PackageName::new("foo", "functions", None);
-    let mut package = wit_encoder::Package::new(name);
+fn concrete_types() {
+    let name = PackageName::new("foo", "functions", None);
+    let mut package = Package::new(name);
 
     package.interface({
-        let mut interface = wit_encoder::Interface::new("functions");
+        let mut interface = Interface::new("functions");
         interface.function(StandaloneFunc::new("f1"));
         interface.function({
             let mut func = StandaloneFunc::new("f2");
@@ -34,55 +35,54 @@ fn smoke() {
             func
         });
         interface.function({
-            let mut func = StandaloneFunc::new("f3");
-            func.results(Type::U32);
+            let mut func = StandaloneFunc::new("f4");
+            func.results(Results::anon(Type::U32));
             func
         });
         interface.function({
-            let mut func = StandaloneFunc::new("f4");
-            func.results(Type::tuple(vec![Type::U32, Type::U32]));
-            func.docs(Some("this is a documentation comment\nfor the f4 function"));
+            let mut func = StandaloneFunc::new("f6");
+            func.results(Results::anon(Type::tuple(vec![Type::U32, Type::U32])));
             func
         });
         interface.function({
-            let mut func = StandaloneFunc::new("f5");
+            let mut func = StandaloneFunc::new("f7");
             func.params(Params::from_iter([("a", Type::F32), ("b", Type::F32)]));
             func.results(Type::tuple(vec![Type::U32, Type::U32]));
             func
         });
         interface.function({
-            let mut func = StandaloneFunc::new("f6");
+            let mut func = StandaloneFunc::new("f8");
             func.params(Params::from_iter([("a", Type::option(Type::U32))]));
             func.results(Type::result(Result_::both(Type::U32, Type::F32)));
             func
         });
         interface.function({
-            let mut func = StandaloneFunc::new("f7");
+            let mut func = StandaloneFunc::new("f9");
             func.results(Results::named(vec![("u", Type::U32), ("f", Type::F32)]));
             func
         });
         interface.function({
-            let mut func = StandaloneFunc::new("f8");
+            let mut func = StandaloneFunc::new("f10");
             func.results(Results::named(vec![("u", Type::U32)]));
             func
         });
         interface.function({
-            let mut func = StandaloneFunc::new("f9");
+            let mut func = StandaloneFunc::new("f11");
             func.results(Type::result(Result_::ok(Type::F32)));
             func
         });
         interface.function({
-            let mut func = StandaloneFunc::new("f10");
+            let mut func = StandaloneFunc::new("f12");
             func.results(Type::result(Result_::err(Type::F32)));
             func
         });
         interface.function({
-            let mut func = StandaloneFunc::new("f11");
+            let mut func = StandaloneFunc::new("f13");
             func.results(Type::result(Result_::empty()));
             func
         });
         interface
     });
 
-    assert_eq!(PACKAGE, package.to_string());
+    assert_eq!(package.to_string(), PACKAGE);
 }
diff --git a/crates/wit-encoder/tests/import-export-overlap1.rs b/crates/wit-encoder/tests/import-export-overlap1.rs
new file mode 100644
index 00000000..e4a57125
--- /dev/null
+++ b/crates/wit-encoder/tests/import-export-overlap1.rs
@@ -0,0 +1,23 @@
+use pretty_assertions::assert_eq;
+use wit_encoder::{Package, PackageName, StandaloneFunc, World};
+
+const PACKAGE: &str = indoc::indoc! {"
+    package foo:foo;
+    
+    world foo {
+      import a: func();
+      export a: func();
+    }
+"};
+
+#[test]
+fn concrete_types() {
+    let mut package = Package::new(PackageName::new("foo", "foo", None));
+
+    let mut world = World::new("foo");
+    world.function_import(StandaloneFunc::new("a"));
+    world.function_export(StandaloneFunc::new("a"));
+    package.world(world);
+
+    assert_eq!(package.to_string(), PACKAGE);
+}
diff --git a/crates/wit-encoder/tests/import-export-overlap2.rs b/crates/wit-encoder/tests/import-export-overlap2.rs
new file mode 100644
index 00000000..da98ca1e
--- /dev/null
+++ b/crates/wit-encoder/tests/import-export-overlap2.rs
@@ -0,0 +1,23 @@
+use pretty_assertions::assert_eq;
+use wit_encoder::{Interface, Package, PackageName, StandaloneFunc, World};
+
+const PACKAGE: &str = indoc::indoc! {"
+    package foo:foo;
+
+    world foo {
+      import a: func();
+      export a: interface {}
+    }
+"};
+
+#[test]
+fn concrete_types() {
+    let mut package = Package::new(PackageName::new("foo", "foo", None));
+
+    let mut world = World::new("foo");
+    world.function_import(StandaloneFunc::new("a"));
+    world.inline_interface_export(Interface::new("a"));
+    package.world(world);
+
+    assert_eq!(package.to_string(), PACKAGE);
+}
diff --git a/crates/wit-encoder/tests/include-reps.rs b/crates/wit-encoder/tests/include-reps.rs
new file mode 100644
index 00000000..5341e053
--- /dev/null
+++ b/crates/wit-encoder/tests/include-reps.rs
@@ -0,0 +1,42 @@
+use pretty_assertions::assert_eq;
+use wit_encoder::{Interface, Package, PackageName, World};
+
+const PACKAGE: &str = indoc::indoc! {"
+    package foo:foo;
+
+    interface a {}
+
+    interface b {}
+
+    world bar {
+      import a;
+      export b;
+    }
+
+    world foo {
+      include bar;
+      include bar;
+      include bar;
+    }
+"};
+
+#[test]
+fn concrete_types() {
+    let mut package = Package::new(PackageName::new("foo", "foo", None));
+
+    package.interface(Interface::new("a"));
+    package.interface(Interface::new("b"));
+
+    let mut world = World::new("bar");
+    world.named_interface_import("a");
+    world.named_interface_export("b");
+    package.world(world);
+
+    let mut world = World::new("foo");
+    world.include("bar");
+    world.include("bar");
+    world.include("bar");
+    package.world(world);
+
+    assert_eq!(package.to_string(), PACKAGE);
+}
diff --git a/crates/wit-encoder/tests/type_defs.rs b/crates/wit-encoder/tests/type_defs.rs
index c4723f9e..fb2d211a 100644
--- a/crates/wit-encoder/tests/type_defs.rs
+++ b/crates/wit-encoder/tests/type_defs.rs
@@ -3,109 +3,109 @@ use wit_encoder::{
     Field, Flag, Params, ResourceFunc, Result_, Results, Type, TypeDef, TypeDefKind, VariantCase,
 };
 
-const PACKAGE: &str = "package wit-encoder:tests;
+const PACKAGE: &str = indoc::indoc! {"
+    package wit-encoder:tests;
 
-/// interface documentation
-interface type-defs {
-    type t1 = u8;
-    type t2 = u16;
-    type t3 = u32;
-    type t4 = u64;
-    type t5 = s8;
-    type t6 = s16;
-    type t7 = s32;
-    type t8 = s64;
-    type t9a = f32;
-    type t9b = f32;
-    type t10a = f64;
-    type t10b = f64;
-    type t11 = char;
-    type t12 = list<char>;
-    type t13 = string;
-    type t14 = option<u32>;
-    type t15 = result<u32, u32>;
-    type t16 = result<_, u32>;
-    type t17 = result<u32>;
-    /// this is a documentation comment
-    type t18 = result;
-    record t20 {
-    }
-    record t21 {
+    /// interface documentation
+    interface type-defs {
+      type t1 = u8;
+      type t2 = u16;
+      type t3 = u32;
+      type t4 = u64;
+      type t5 = s8;
+      type t6 = s16;
+      type t7 = s32;
+      type t8 = s64;
+      type t9a = f32;
+      type t9b = f32;
+      type t10a = f64;
+      type t10b = f64;
+      type t11 = char;
+      type t12 = list<char>;
+      type t13 = string;
+      type t14 = option<u32>;
+      type t15 = result<u32, u32>;
+      type t16 = result<_, u32>;
+      type t17 = result<u32>;
+      /// this is a documentation comment
+      type t18 = result;
+      record t20 {  }
+      record t21 {
         a: u32,
-    }
-    record t22 {
+      }
+      record t22 {
         a: u32,
-    }
-    record t23 {
+      }
+      record t23 {
         a: u32,
         b: u64,
-    }
-    record t24 {
+      }
+      record t24 {
         a: u32,
         b: u64,
-    }
-    record t25 {
+      }
+      record t25 {
         x: u32,
-    }
-    record %record {
+      }
+      record %record {
         a: u32,
-    }
-    type t26 = tuple<>;
-    type t27 = tuple<u32>;
-    type t29 = tuple<u32, u64>;
-    flags t30 {
-    }
-    flags t31 {
+      }
+      type t26 = tuple<>;
+      type t27 = tuple<u32>;
+      type t29 = tuple<u32, u64>;
+      flags t30 {
+      }
+      flags t31 {
         /// option a
         a,
         /// option b
         b,
         /// option c
         c,
-    }
-    flags t32 {
+      }
+      flags t32 {
         a,
         b,
         c,
-    }
-    variant t33 {
+      }
+      variant t33 {
         a,
-    }
-    variant t34 {
+      }
+      variant t34 {
         a,
         b,
-    }
-    variant t35 {
+      }
+      variant t35 {
         a,
         b,
-    }
-    variant t36 {
+      }
+      variant t36 {
         a,
         b(u32),
-    }
-    variant t37 {
+      }
+      variant t37 {
         a,
         b(option<u32>),
-    }
-    enum t41 {
+      }
+      enum t41 {
         a,
         b,
         c,
-    }
-    enum t42 {
+      }
+      enum t42 {
         a,
         b,
         c,
-    }
-    type t43 = bool;
-    type t44 = string;
-    type t45 = list<list<list<t32>>>;
-    type t46 = t44;
-    type foo = bar;
-    type bar = u32;
-    resource t50 {
-    }
-    resource t51 {
+      }
+      type t43 = bool;
+      type t44 = string;
+      type t45 = list<list<list<t32>>>;
+      type t46 = t44;
+      type foo = bar;
+      type bar = u32;
+      resource t50 {
+      }
+      resource t51 {
         /// create a new t51
         constructor(a: u32);
         /// set a
@@ -114,9 +114,9 @@ interface type-defs {
         get-a: func() -> u32;
         /// do b
         b: static func();
+      }
     }
-}
-";
+"};
 
 #[test]
 fn types() {
diff --git a/crates/wit-encoder/tests/world.rs b/crates/wit-encoder/tests/world.rs
index 38e71bb9..83777cc0 100644
--- a/crates/wit-encoder/tests/world.rs
+++ b/crates/wit-encoder/tests/world.rs
@@ -1,22 +1,23 @@
 use pretty_assertions::assert_eq;
 use wit_encoder::{Interface, StandaloneFunc, Type};
 
-const PACKAGE: &str = "package foo:functions;
+const PACKAGE: &str = indoc::indoc! {"
+    package foo:functions;
 
-interface error-reporter {
-}
-world %world {
-    /// inline interface
-    export example: interface {
+    interface error-reporter {}
+
+    world %world {
+      /// inline interface
+      export example: interface {
         /// func docs
         do-nothing: func();
+      }
+      /// scan stuff
+      export scan: func() -> list<u8>;
+      import error-reporter;
+      import print: func(s: string);
     }
-    /// scan stuff
-    export scan: func() -> list<u8>;
-    import error-reporter;
-    import print: func(s: string);
-}
-";
+"};
 
 #[test]
 fn worlds() {