Repository: bytecodealliance/wasm-tools
Pull request #2060 (2025-02-20 21:39:53)

Refactor how `error-context` is modeled
This commit refactors how the `error-context` type in the async component model implementation is modeled within these crates. Previously it was listed as a "defined type" but the binary encoding and component model itself has it listed as a "primitive type". While mostly the same the distinction is subtle enough (e.g. around naming rules) that this commit refactors the wasm-tools crates to match the component model specification.


Patch:
diff --git a/crates/wasm-compose/src/encoding.rs b/crates/wasm-compose/src/encoding.rs
index bcbcfd89..fe1622e7 100644
--- a/crates/wasm-compose/src/encoding.rs
+++ b/crates/wasm-compose/src/encoding.rs
@@ -668,7 +668,6 @@ impl<'a> TypeEncoder<'a> {
             }
             ComponentDefinedType::Future(ty) => self.future(state, *ty),
             ComponentDefinedType::Stream(ty) => self.stream(state, *ty),
-            ComponentDefinedType::ErrorContext => self.error_context(state),
         }
     }
 
@@ -806,12 +805,6 @@ impl<'a> TypeEncoder<'a> {
         state.cur.encodable.ty().defined_type().stream(ty);
         index
     }
-
-    fn error_context(&self, state: &mut TypeState<'a>) -> u32 {
-        let index = state.cur.encodable.type_count();
-        state.cur.encodable.ty().defined_type().error_context();
-        index
-    }
 }
 
 /// Represents an instance index in a composition graph.
@@ -1234,8 +1227,7 @@ impl DependencyRegistrar<'_, '_> {
         match &self.types[ty] {
             ComponentDefinedType::Primitive(_)
             | ComponentDefinedType::Enum(_)
-            | ComponentDefinedType::Flags(_)
-            | ComponentDefinedType::ErrorContext => {}
+            | ComponentDefinedType::Flags(_) => {}
             ComponentDefinedType::List(t) | ComponentDefinedType::Option(t) => self.val_type(*t),
             ComponentDefinedType::Own(r) | ComponentDefinedType::Borrow(r) => {
                 self.ty(ComponentAnyTypeId::Resource(*r))
diff --git a/crates/wasm-encoder/src/component/types.rs b/crates/wasm-encoder/src/component/types.rs
index 43285e71..bb09ff43 100644
--- a/crates/wasm-encoder/src/component/types.rs
+++ b/crates/wasm-encoder/src/component/types.rs
@@ -490,6 +490,8 @@ pub enum PrimitiveValType {
     Char,
     /// The type is a string.
     String,
+    /// Type for `error-context` added with async support in the component model.
+    ErrorContext,
 }
 
 impl Encode for PrimitiveValType {
@@ -508,6 +510,7 @@ impl Encode for PrimitiveValType {
             Self::F64 => 0x75,
             Self::Char => 0x74,
             Self::String => 0x73,
+            Self::ErrorContext => 0x64,
         });
     }
 }
@@ -665,11 +668,6 @@ impl ComponentDefinedTypeEncoder<'_> {
         self.0.push(0x66);
         payload.encode(self.0);
     }
-
-    /// Define the `error-context` type.
-    pub fn error_context(self) {
-        self.0.push(0x64);
-    }
 }
 
 /// An encoder for the type section of WebAssembly components.
diff --git a/crates/wasm-encoder/src/reencode/component.rs b/crates/wasm-encoder/src/reencode/component.rs
index 91c53e5a..4401cf9d 100644
--- a/crates/wasm-encoder/src/reencode/component.rs
+++ b/crates/wasm-encoder/src/reencode/component.rs
@@ -796,7 +796,6 @@ pub mod component_utils {
             wasmparser::ComponentDefinedType::Stream(t) => {
                 defined.stream(t.map(|t| reencoder.component_val_type(t)));
             }
-            wasmparser::ComponentDefinedType::ErrorContext => defined.error_context(),
         }
         Ok(())
     }
@@ -1238,6 +1237,9 @@ pub mod component_utils {
             wasmparser::PrimitiveValType::F64 => crate::component::PrimitiveValType::F64,
             wasmparser::PrimitiveValType::Char => crate::component::PrimitiveValType::Char,
             wasmparser::PrimitiveValType::String => crate::component::PrimitiveValType::String,
+            wasmparser::PrimitiveValType::ErrorContext => {
+                crate::component::PrimitiveValType::ErrorContext
+            }
         }
     }
 
diff --git a/crates/wasm-smith/src/component.rs b/crates/wasm-smith/src/component.rs
index 9428a5f5..16d8225b 100644
--- a/crates/wasm-smith/src/component.rs
+++ b/crates/wasm-smith/src/component.rs
@@ -1784,7 +1784,7 @@ fn canonical_abi_for(func_ty: &FuncType) -> Rc<crate::core::FuncType> {
             PrimitiveValType::S64 | PrimitiveValType::U64 => ValType::I64,
             PrimitiveValType::F32 => ValType::F32,
             PrimitiveValType::F64 => ValType::F64,
-            PrimitiveValType::String => {
+            PrimitiveValType::String | PrimitiveValType::ErrorContext => {
                 unimplemented!("non-scalar types are not supported yet")
             }
         },
@@ -2067,7 +2067,7 @@ fn is_scalar(ty: &ComponentValType) -> bool {
             | PrimitiveValType::F32
             | PrimitiveValType::F64
             | PrimitiveValType::Char => true,
-            PrimitiveValType::String => false,
+            PrimitiveValType::String | PrimitiveValType::ErrorContext => false,
         },
         ComponentValType::Type(_) => false,
     }
diff --git a/crates/wasmparser/src/readers/component/types.rs b/crates/wasmparser/src/readers/component/types.rs
index dfcd4e06..a9ce0eb2 100644
--- a/crates/wasmparser/src/readers/component/types.rs
+++ b/crates/wasmparser/src/readers/component/types.rs
@@ -185,6 +185,9 @@ pub enum PrimitiveValType {
     Char,
     /// The type is a string.
     String,
+    /// The error-context type. (added with the async proposal for the component
+    /// model)
+    ErrorContext,
 }
 
 impl PrimitiveValType {
@@ -203,6 +206,7 @@ impl PrimitiveValType {
             0x75 => PrimitiveValType::F64,
             0x74 => PrimitiveValType::Char,
             0x73 => PrimitiveValType::String,
+            0x64 => PrimitiveValType::ErrorContext,
             _ => return None,
         })
     }
@@ -241,6 +245,7 @@ impl fmt::Display for PrimitiveValType {
             F64 => "f64",
             Char => "char",
             String => "string",
+            ErrorContext => "error-context",
         };
         s.fmt(f)
     }
@@ -455,8 +460,6 @@ pub enum ComponentDefinedType<'a> {
     Future(Option<ComponentValType>),
     /// A stream type with the specified payload type.
     Stream(Option<ComponentValType>),
-    /// The error-context type.
-    ErrorContext,
 }
 
 impl<'a> ComponentDefinedType<'a> {
@@ -498,7 +501,6 @@ impl<'a> ComponentDefinedType<'a> {
             0x68 => ComponentDefinedType::Borrow(reader.read()?),
             0x65 => ComponentDefinedType::Future(reader.read()?),
             0x66 => ComponentDefinedType::Stream(reader.read()?),
-            0x64 => ComponentDefinedType::ErrorContext,
             x => return reader.invalid_leading_byte(x, "component defined type"),
         })
     }
diff --git a/crates/wasmparser/src/validator/component.rs b/crates/wasmparser/src/validator/component.rs
index c963abb7..51411d04 100644
--- a/crates/wasmparser/src/validator/component.rs
+++ b/crates/wasmparser/src/validator/component.rs
@@ -721,8 +721,7 @@ impl ComponentState {
             // named.
             ComponentDefinedType::Primitive(_)
             | ComponentDefinedType::Flags(_)
-            | ComponentDefinedType::Enum(_)
-            | ComponentDefinedType::ErrorContext => true,
+            | ComponentDefinedType::Enum(_) => true,
 
             // Referenced types of all these aggregates must all be
             // named.
@@ -3292,7 +3291,6 @@ impl ComponentState {
                 ty.map(|ty| self.create_component_val_type(ty, offset))
                     .transpose()?,
             )),
-            crate::ComponentDefinedType::ErrorContext => Ok(ComponentDefinedType::ErrorContext),
         }
     }
 
diff --git a/crates/wasmparser/src/validator/component_types.rs b/crates/wasmparser/src/validator/component_types.rs
index bfea0fc0..f76ca674 100644
--- a/crates/wasmparser/src/validator/component_types.rs
+++ b/crates/wasmparser/src/validator/component_types.rs
@@ -123,7 +123,8 @@ fn push_primitive_wasm_types(ty: &PrimitiveValType, lowered_types: &mut LoweredT
         | PrimitiveValType::U16
         | PrimitiveValType::S32
         | PrimitiveValType::U32
-        | PrimitiveValType::Char => lowered_types.push(ValType::I32),
+        | PrimitiveValType::Char
+        | PrimitiveValType::ErrorContext => lowered_types.push(ValType::I32),
         PrimitiveValType::S64 | PrimitiveValType::U64 => lowered_types.push(ValType::I64),
         PrimitiveValType::F32 => lowered_types.push(ValType::F32),
         PrimitiveValType::F64 => lowered_types.push(ValType::F64),
@@ -1065,8 +1066,6 @@ pub enum ComponentDefinedType {
     Future(Option<ComponentValType>),
     /// A stream type with the specified payload type.
     Stream(Option<ComponentValType>),
-    /// The error-context type.
-    ErrorContext,
 }
 
 impl TypeData for ComponentDefinedType {
@@ -1079,8 +1078,7 @@ impl TypeData for ComponentDefinedType {
             | Self::Enum(_)
             | Self::Own(_)
             | Self::Future(_)
-            | Self::Stream(_)
-            | Self::ErrorContext => TypeInfo::new(),
+            | Self::Stream(_) => TypeInfo::new(),
             Self::Borrow(_) => TypeInfo::borrow(),
             Self::Record(r) => r.info,
             Self::Variant(v) => v.info,
@@ -1113,8 +1111,7 @@ impl ComponentDefinedType {
             | Self::Own(_)
             | Self::Borrow(_)
             | Self::Future(_)
-            | Self::Stream(_)
-            | Self::ErrorContext => false,
+            | Self::Stream(_) => false,
             Self::Option(ty) => ty.contains_ptr(types),
             Self::Result { ok, err } => {
                 ok.map(|ty| ty.contains_ptr(types)).unwrap_or(false)
@@ -1143,12 +1140,9 @@ impl ComponentDefinedType {
             Self::Flags(names) => {
                 (0..(names.len() + 31) / 32).all(|_| lowered_types.push(ValType::I32))
             }
-            Self::Enum(_)
-            | Self::Own(_)
-            | Self::Borrow(_)
-            | Self::Future(_)
-            | Self::Stream(_)
-            | Self::ErrorContext => lowered_types.push(ValType::I32),
+            Self::Enum(_) | Self::Own(_) | Self::Borrow(_) | Self::Future(_) | Self::Stream(_) => {
+                lowered_types.push(ValType::I32)
+            }
             Self::Option(ty) => {
                 Self::push_variant_wasm_types([ty].into_iter(), types, lowered_types)
             }
@@ -1218,7 +1212,6 @@ impl ComponentDefinedType {
             ComponentDefinedType::Borrow(_) => "borrow",
             ComponentDefinedType::Future(_) => "future",
             ComponentDefinedType::Stream(_) => "stream",
-            ComponentDefinedType::ErrorContext => "error-context",
         }
     }
 }
@@ -1933,8 +1926,7 @@ impl TypeAlloc {
         match &self[id] {
             ComponentDefinedType::Primitive(_)
             | ComponentDefinedType::Flags(_)
-            | ComponentDefinedType::Enum(_)
-            | ComponentDefinedType::ErrorContext => {}
+            | ComponentDefinedType::Enum(_) => {}
             ComponentDefinedType::Record(r) => {
                 for ty in r.fields.values() {
                     self.free_variables_valtype(ty, set);
@@ -2071,7 +2063,7 @@ impl TypeAlloc {
         let ty = &self[id];
         match ty {
             // Primitives are always considered named
-            ComponentDefinedType::Primitive(_) | ComponentDefinedType::ErrorContext => true,
+            ComponentDefinedType::Primitive(_) => true,
 
             // These structures are never allowed to be anonymous, so they
             // themselves must be named.
@@ -2263,8 +2255,7 @@ where
         match &mut tmp {
             ComponentDefinedType::Primitive(_)
             | ComponentDefinedType::Flags(_)
-            | ComponentDefinedType::Enum(_)
-            | ComponentDefinedType::ErrorContext => {}
+            | ComponentDefinedType::Enum(_) => {}
             ComponentDefinedType::Record(r) => {
                 for ty in r.fields.values_mut() {
                     any_changed |= self.remap_valtype(ty, map);
@@ -3241,8 +3232,6 @@ impl<'a> SubtypeCx<'a> {
                 (Some(_), None) => bail!(offset, "expected stream type to not be present"),
             },
             (Stream(_), b) => bail!(offset, "expected {}, found stream", b.desc()),
-            (ErrorContext, ErrorContext) => Ok(()),
-            (ErrorContext, b) => bail!(offset, "expected {}, found error-context", b.desc()),
         }
     }
 
diff --git a/crates/wasmprinter/src/component.rs b/crates/wasmprinter/src/component.rs
index d7028932..64378bd8 100644
--- a/crates/wasmprinter/src/component.rs
+++ b/crates/wasmprinter/src/component.rs
@@ -105,6 +105,7 @@ impl Printer<'_, '_> {
             PrimitiveValType::F64 => self.print_type_keyword("f64")?,
             PrimitiveValType::Char => self.print_type_keyword("char")?,
             PrimitiveValType::String => self.print_type_keyword("string")?,
+            PrimitiveValType::ErrorContext => self.print_type_keyword("error-context")?,
         }
         Ok(())
     }
@@ -280,7 +281,6 @@ impl Printer<'_, '_> {
             }
             ComponentDefinedType::Future(ty) => self.print_future_type(state, *ty)?,
             ComponentDefinedType::Stream(ty) => self.print_stream_type(state, *ty)?,
-            ComponentDefinedType::ErrorContext => self.print_type_keyword("error-context")?,
         }
 
         Ok(())
diff --git a/crates/wast/src/component/binary.rs b/crates/wast/src/component/binary.rs
index ac4f6682..2fc95b97 100644
--- a/crates/wast/src/component/binary.rs
+++ b/crates/wast/src/component/binary.rs
@@ -740,6 +740,7 @@ impl From<PrimitiveValType> for wasm_encoder::PrimitiveValType {
             PrimitiveValType::F64 => Self::F64,
             PrimitiveValType::Char => Self::Char,
             PrimitiveValType::String => Self::String,
+            PrimitiveValType::ErrorContext => Self::ErrorContext,
         }
     }
 }
diff --git a/crates/wast/src/component/types.rs b/crates/wast/src/component/types.rs
index 70fd05ca..38c88a38 100644
--- a/crates/wast/src/component/types.rs
+++ b/crates/wast/src/component/types.rs
@@ -238,6 +238,7 @@ pub enum PrimitiveValType {
     F64,
     Char,
     String,
+    ErrorContext,
 }
 
 impl<'a> Parse<'a> for PrimitiveValType {
@@ -288,6 +289,9 @@ impl<'a> Parse<'a> for PrimitiveValType {
         } else if l.peek::<kw::string>()? {
             parser.parse::<kw::string>()?;
             Ok(Self::String)
+        } else if l.peek::<kw::error_context>()? {
+            parser.parse::<kw::error_context>()?;
+            Ok(Self::ErrorContext)
         } else {
             Err(l.error())
         }
diff --git a/crates/wast/src/lib.rs b/crates/wast/src/lib.rs
index 632dee3d..0cd276e8 100644
--- a/crates/wast/src/lib.rs
+++ b/crates/wast/src/lib.rs
@@ -585,6 +585,7 @@ pub mod kw {
     custom_keyword!(callback);
     custom_keyword!(stream);
     custom_keyword!(future);
+    custom_keyword!(error_context = "error-context");
 }
 
 /// Common annotations used to parse WebAssembly text files.
diff --git a/crates/wit-component/src/encoding.rs b/crates/wit-component/src/encoding.rs
index 5d7c006a..b7c1afdd 100644
--- a/crates/wit-component/src/encoding.rs
+++ b/crates/wit-component/src/encoding.rs
@@ -323,7 +323,6 @@ impl TypeContents {
                 TypeDefKind::Type(t) => Self::for_type(resolve, t),
                 TypeDefKind::Future(_) => Self::empty(),
                 TypeDefKind::Stream(_) => Self::empty(),
-                TypeDefKind::ErrorContext => Self::empty(),
                 TypeDefKind::Unknown => unreachable!(),
             },
             Type::String => Self::STRING,
diff --git a/crates/wit-component/src/encoding/types.rs b/crates/wit-component/src/encoding/types.rs
index 9cee8e09..59d4330f 100644
--- a/crates/wit-component/src/encoding/types.rs
+++ b/crates/wit-component/src/encoding/types.rs
@@ -112,6 +112,7 @@ pub trait ValtypeEncoder<'a> {
             Type::F64 => ComponentValType::Primitive(PrimitiveValType::F64),
             Type::Char => ComponentValType::Primitive(PrimitiveValType::Char),
             Type::String => ComponentValType::Primitive(PrimitiveValType::String),
+            Type::ErrorContext => ComponentValType::Primitive(PrimitiveValType::ErrorContext),
             Type::Id(id) => {
                 // If this id has already been prior defined into this section
                 // refer to that definition.
@@ -146,7 +147,6 @@ pub trait ValtypeEncoder<'a> {
                     TypeDefKind::Type(ty) => self.encode_valtype(resolve, ty)?,
                     TypeDefKind::Future(ty) => self.encode_future(resolve, ty)?,
                     TypeDefKind::Stream(ty) => self.encode_stream(resolve, ty)?,
-                    TypeDefKind::ErrorContext => self.encode_error_context()?,
                     TypeDefKind::Unknown => unreachable!(),
                     TypeDefKind::Resource => {
                         let name = ty.name.as_ref().expect("resources must be named");
@@ -323,12 +323,6 @@ pub trait ValtypeEncoder<'a> {
         encoder.stream(ty);
         Ok(ComponentValType::Type(index))
     }
-
-    fn encode_error_context(&mut self) -> Result<ComponentValType> {
-        let (index, encoder) = self.defined_type();
-        encoder.error_context();
-        Ok(ComponentValType::Type(index))
-    }
 }
 
 pub struct RootTypeEncoder<'state, 'a> {
diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs
index 3970214d..d4414910 100644
--- a/crates/wit-component/src/printing.rs
+++ b/crates/wit-component/src/printing.rs
@@ -514,6 +514,7 @@ impl<O: Output> WitPrinter<O> {
             }
             Type::Char => self.output.ty("char", TypeKind::BuiltIn),
             Type::String => self.output.ty("string", TypeKind::BuiltIn),
+            Type::ErrorContext => self.output.ty("error-context", TypeKind::BuiltIn),
 
             Type::Id(id) => {
                 let ty = &resolve.types[*id];
@@ -575,7 +576,6 @@ impl<O: Output> WitPrinter<O> {
                             self.output.push_str("stream");
                         }
                     }
-                    TypeDefKind::ErrorContext => self.output.push_str("error-context"),
                     TypeDefKind::Unknown => unreachable!(),
                 }
             }
@@ -703,7 +703,8 @@ impl<O: Output> WitPrinter<O> {
             | Type::F32
             | Type::F64
             | Type::Char
-            | Type::String => return Ok(()),
+            | Type::String
+            | Type::ErrorContext => return Ok(()),
 
             Type::Id(id) => {
                 let ty = &resolve.types[*id];
@@ -747,7 +748,6 @@ impl<O: Output> WitPrinter<O> {
                     TypeDefKind::Stream(inner) => {
                         self.declare_stream(resolve, ty.name.as_deref(), inner.as_ref())?
                     }
-                    TypeDefKind::ErrorContext => self.declare_error_context(ty.name.as_deref())?,
                     TypeDefKind::Unknown => unreachable!(),
                 }
             }
@@ -990,19 +990,6 @@ impl<O: Output> WitPrinter<O> {
         Ok(())
     }
 
-    fn declare_error_context(&mut self, name: Option<&str>) -> Result<()> {
-        if let Some(name) = name {
-            self.output.keyword("type");
-            self.output.str(" ");
-            self.print_name_type(name, TypeKind::ErrorContext);
-            self.output.str(" = ");
-            self.output.ty("error-context", TypeKind::BuiltIn);
-            self.output.semicolon();
-        }
-
-        Ok(())
-    }
-
     fn escape_name(name: &str) -> Cow<str> {
         if is_keyword(name) {
             Cow::Owned(format!("%{name}"))
diff --git a/crates/wit-encoder/src/from_parser.rs b/crates/wit-encoder/src/from_parser.rs
index 6e7e4302..a5aae5b7 100644
--- a/crates/wit-encoder/src/from_parser.rs
+++ b/crates/wit-encoder/src/from_parser.rs
@@ -234,7 +234,6 @@ impl<'a> Converter<'a> {
                     wit_parser::TypeDefKind::Stream(ty) => {
                         TypeDefKind::Type(Type::stream(self.convert_option_type(ty)))
                     }
-                    wit_parser::TypeDefKind::ErrorContext => TypeDefKind::Type(Type::ErrorContext),
                     // all the following are just `type` declarations
                     wit_parser::TypeDefKind::Option(ty) => {
                         let output = Type::option(self.convert_type(ty));
@@ -287,6 +286,7 @@ impl<'a> Converter<'a> {
             wit_parser::Type::F64 => Type::F64,
             wit_parser::Type::Char => Type::Char,
             wit_parser::Type::String => Type::String,
+            wit_parser::Type::ErrorContext => Type::ErrorContext,
             wit_parser::Type::Id(id) => {
                 let type_def = self.resolve.types.get(*id).expect("Type not found");
                 match &type_def.name {
@@ -311,7 +311,6 @@ impl<'a> Converter<'a> {
                         wit_parser::TypeDefKind::Stream(type_) => {
                             Type::stream(self.convert_option_type(type_))
                         }
-                        wit_parser::TypeDefKind::ErrorContext => Type::ErrorContext,
                         wit_parser::TypeDefKind::Record(_)
                         | wit_parser::TypeDefKind::Resource
                         | wit_parser::TypeDefKind::Flags(_)
diff --git a/crates/wit-parser/src/abi.rs b/crates/wit-parser/src/abi.rs
index 4af7dffa..d3990448 100644
--- a/crates/wit-parser/src/abi.rs
+++ b/crates/wit-parser/src/abi.rs
@@ -245,7 +245,8 @@ impl Resolve {
             | Type::U16
             | Type::S32
             | Type::U32
-            | Type::Char => result.push(WasmType::I32),
+            | Type::Char
+            | Type::ErrorContext => result.push(WasmType::I32),
 
             Type::U64 | Type::S64 => result.push(WasmType::I64),
             Type::F32 => result.push(WasmType::F32),
@@ -312,10 +313,6 @@ impl Resolve {
                     result.push(WasmType::I32);
                 }
 
-                TypeDefKind::ErrorContext => {
-                    result.push(WasmType::I32);
-                }
-
                 TypeDefKind::Unknown => unreachable!(),
             },
         }
diff --git a/crates/wit-parser/src/ast/resolve.rs b/crates/wit-parser/src/ast/resolve.rs
index 59fdba0f..44620365 100644
--- a/crates/wit-parser/src/ast/resolve.rs
+++ b/crates/wit-parser/src/ast/resolve.rs
@@ -96,7 +96,6 @@ enum Key {
     Result(Option<Type>, Option<Type>),
     Future(Option<Type>),
     Stream(Option<Type>),
-    ErrorContext,
 }
 
 enum TypeItem<'a, 'b> {
@@ -1142,6 +1141,7 @@ impl<'a> Resolver<'a> {
             ast::Type::F64(_) => TypeDefKind::Type(Type::F64),
             ast::Type::Char(_) => TypeDefKind::Type(Type::Char),
             ast::Type::String(_) => TypeDefKind::Type(Type::String),
+            ast::Type::ErrorContext(_) => TypeDefKind::Type(Type::ErrorContext),
             ast::Type::Name(name) => {
                 let id = self.resolve_type_name(name)?;
                 TypeDefKind::Type(Type::Id(id))
@@ -1259,7 +1259,6 @@ impl<'a> Resolver<'a> {
             ast::Type::Stream(s) => {
                 TypeDefKind::Stream(self.resolve_optional_type(s.ty.as_deref(), stability)?)
             }
-            ast::Type::ErrorContext(_) => TypeDefKind::ErrorContext,
         })
     }
 
@@ -1339,8 +1338,7 @@ impl<'a> Resolver<'a> {
                 }
                 // Assume these are named types which will be annotated with an
                 // explicit stability if applicable:
-                TypeDefKind::ErrorContext
-                | TypeDefKind::Resource
+                TypeDefKind::Resource
                 | TypeDefKind::Variant(_)
                 | TypeDefKind::Record(_)
                 | TypeDefKind::Flags(_)
@@ -1425,7 +1423,6 @@ impl<'a> Resolver<'a> {
             TypeDefKind::Result(r) => Key::Result(r.ok, r.err),
             TypeDefKind::Future(ty) => Key::Future(*ty),
             TypeDefKind::Stream(ty) => Key::Stream(*ty),
-            TypeDefKind::ErrorContext => Key::ErrorContext,
             TypeDefKind::Unknown => unreachable!(),
         };
         let id = self.anon_types.entry(key).or_insert_with(|| {
diff --git a/crates/wit-parser/src/decoding.rs b/crates/wit-parser/src/decoding.rs
index 64a5a690..d9a50d99 100644
--- a/crates/wit-parser/src/decoding.rs
+++ b/crates/wit-parser/src/decoding.rs
@@ -1252,8 +1252,7 @@ impl WitPackageDecoder<'_> {
             | TypeDefKind::Result(_)
             | TypeDefKind::Handle(_)
             | TypeDefKind::Future(_)
-            | TypeDefKind::Stream(_)
-            | TypeDefKind::ErrorContext => {}
+            | TypeDefKind::Stream(_) => {}
 
             TypeDefKind::Resource
             | TypeDefKind::Record(_)
@@ -1392,8 +1391,6 @@ impl WitPackageDecoder<'_> {
             ComponentDefinedType::Stream(ty) => Ok(TypeDefKind::Stream(
                 ty.as_ref().map(|ty| self.convert_valtype(ty)).transpose()?,
             )),
-
-            ComponentDefinedType::ErrorContext => Ok(TypeDefKind::ErrorContext),
         }
     }
 
@@ -1412,6 +1409,7 @@ impl WitPackageDecoder<'_> {
             PrimitiveValType::String => Type::String,
             PrimitiveValType::F32 => Type::F32,
             PrimitiveValType::F64 => Type::F64,
+            PrimitiveValType::ErrorContext => Type::ErrorContext,
         }
     }
 
@@ -1694,8 +1692,7 @@ impl Registrar<'_> {
             ComponentDefinedType::Flags(_)
             | ComponentDefinedType::Enum(_)
             | ComponentDefinedType::Own(_)
-            | ComponentDefinedType::Borrow(_)
-            | ComponentDefinedType::ErrorContext => Ok(()),
+            | ComponentDefinedType::Borrow(_) => Ok(()),
         }
     }
 
diff --git a/crates/wit-parser/src/lib.rs b/crates/wit-parser/src/lib.rs
index dc945735..cc5ee29d 100644
--- a/crates/wit-parser/src/lib.rs
+++ b/crates/wit-parser/src/lib.rs
@@ -571,7 +571,6 @@ pub enum TypeDefKind {
     List(Type),
     Future(Option<Type>),
     Stream(Option<Type>),
-    ErrorContext,
     Type(Type),
 
     /// This represents a type of unknown structure imported from a foreign
@@ -600,7 +599,6 @@ impl TypeDefKind {
             TypeDefKind::List(_) => "list",
             TypeDefKind::Future(_) => "future",
             TypeDefKind::Stream(_) => "stream",
-            TypeDefKind::ErrorContext => "error-context",
             TypeDefKind::Type(_) => "type",
             TypeDefKind::Unknown => "unknown",
         }
@@ -648,6 +646,7 @@ pub enum Type {
     F64,
     Char,
     String,
+    ErrorContext,
     Id(TypeId),
 }
 
@@ -1030,8 +1029,7 @@ fn find_futures_and_streams(resolve: &Resolve, ty: Type, results: &mut Vec<TypeI
         TypeDefKind::Resource
         | TypeDefKind::Handle(_)
         | TypeDefKind::Flags(_)
-        | TypeDefKind::Enum(_)
-        | TypeDefKind::ErrorContext => {}
+        | TypeDefKind::Enum(_) => {}
         TypeDefKind::Record(r) => {
             for Field { ty, .. } in &r.fields {
                 find_futures_and_streams(resolve, *ty, results);
diff --git a/crates/wit-parser/src/live.rs b/crates/wit-parser/src/live.rs
index d7c04306..fd52aaae 100644
--- a/crates/wit-parser/src/live.rs
+++ b/crates/wit-parser/src/live.rs
@@ -161,8 +161,7 @@ pub trait TypeIdVisitor {
                     self.visit_type(resolve, ty);
                 }
             }
-            TypeDefKind::ErrorContext
-            | TypeDefKind::Flags(_)
+            TypeDefKind::Flags(_)
             | TypeDefKind::Enum(_)
             | TypeDefKind::Future(None)
             | TypeDefKind::Stream(None) => {}
diff --git a/crates/wit-parser/src/resolve.rs b/crates/wit-parser/src/resolve.rs
index e2c05d88..764d64a7 100644
--- a/crates/wit-parser/src/resolve.rs
+++ b/crates/wit-parser/src/resolve.rs
@@ -563,7 +563,7 @@ package {name} is defined in two different locations:\n\
             | Type::F32
             | Type::F64 => true,
 
-            Type::Bool | Type::Char | Type::String => false,
+            Type::Bool | Type::Char | Type::String | Type::ErrorContext => false,
 
             Type::Id(id) => match &self.types[*id].kind {
                 TypeDefKind::List(_)
@@ -572,8 +572,7 @@ package {name} is defined in two different locations:\n\
                 | TypeDefKind::Option(_)
                 | TypeDefKind::Result(_)
                 | TypeDefKind::Future(_)
-                | TypeDefKind::Stream(_)
-                | TypeDefKind::ErrorContext => false,
+                | TypeDefKind::Stream(_) => false,
                 TypeDefKind::Type(t) => self.all_bits_valid(t),
 
                 TypeDefKind::Handle(h) => match h {
@@ -3087,7 +3086,6 @@ impl Remap {
                     self.update_ty(resolve, ty, span)?;
                 }
             }
-            ErrorContext => {}
 
             // Note that `update_ty` is specifically not used here as typedefs
             // because for the `type a = b` form that doesn't force `a` to be a
@@ -3474,9 +3472,7 @@ impl Remap {
                 .iter()
                 .filter_map(|t| t.as_ref())
                 .any(|t| self.type_has_borrow(resolve, t)),
-            TypeDefKind::Future(None) | TypeDefKind::Stream(None) | TypeDefKind::ErrorContext => {
-                false
-            }
+            TypeDefKind::Future(None) | TypeDefKind::Stream(None) => false,
             TypeDefKind::Unknown => unreachable!(),
         }
     }
diff --git a/crates/wit-parser/src/resolve/clone.rs b/crates/wit-parser/src/resolve/clone.rs
index 650a7047..cc116937 100644
--- a/crates/wit-parser/src/resolve/clone.rs
+++ b/crates/wit-parser/src/resolve/clone.rs
@@ -105,8 +105,7 @@ impl<'a> Cloner<'a> {
             TypeDefKind::Type(_)
             | TypeDefKind::Resource
             | TypeDefKind::Flags(_)
-            | TypeDefKind::Enum(_)
-            | TypeDefKind::ErrorContext => {}
+            | TypeDefKind::Enum(_) => {}
             TypeDefKind::Handle(Handle::Own(ty) | Handle::Borrow(ty)) => {
                 self.type_id(ty);
             }
diff --git a/crates/wit-parser/src/serde_.rs b/crates/wit-parser/src/serde_.rs
index d7ed36b1..76a14e2b 100644
--- a/crates/wit-parser/src/serde_.rs
+++ b/crates/wit-parser/src/serde_.rs
@@ -72,6 +72,7 @@ impl Serialize for Type {
             Type::F64 => serializer.serialize_str("f64"),
             Type::Char => serializer.serialize_str("char"),
             Type::String => serializer.serialize_str("string"),
+            Type::ErrorContext => serializer.serialize_str("error-context"),
             Type::Id(type_id) => serializer.serialize_u64(type_id.index() as u64),
         }
     }
diff --git a/crates/wit-parser/src/sizealign.rs b/crates/wit-parser/src/sizealign.rs
index 6dd8706c..5e0f0e0d 100644
--- a/crates/wit-parser/src/sizealign.rs
+++ b/crates/wit-parser/src/sizealign.rs
@@ -281,10 +281,9 @@ impl SizeAlign {
             // A future is represented as an index.
             // A stream is represented as an index.
             // An error is represented as an index.
-            TypeDefKind::Handle(_)
-            | TypeDefKind::Future(_)
-            | TypeDefKind::Stream(_)
-            | TypeDefKind::ErrorContext => int_size_align(Int::U32),
+            TypeDefKind::Handle(_) | TypeDefKind::Future(_) | TypeDefKind::Stream(_) => {
+                int_size_align(Int::U32)
+            }
             // This shouldn't be used for anything since raw resources aren't part of the ABI -- just handles to
             // them.
             TypeDefKind::Resource => ElementInfo::new(
@@ -299,7 +298,9 @@ impl SizeAlign {
         match ty {
             Type::Bool | Type::U8 | Type::S8 => ArchitectureSize::new(1, 0),
             Type::U16 | Type::S16 => ArchitectureSize::new(2, 0),
-            Type::U32 | Type::S32 | Type::F32 | Type::Char => ArchitectureSize::new(4, 0),
+            Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => {
+                ArchitectureSize::new(4, 0)
+            }
             Type::U64 | Type::S64 | Type::F64 => ArchitectureSize::new(8, 0),
             Type::String => ArchitectureSize::new(0, 2),
             Type::Id(id) => self.map[id.index()].size,
@@ -310,7 +311,7 @@ impl SizeAlign {
         match ty {
             Type::Bool | Type::U8 | Type::S8 => Alignment::Bytes(NonZeroUsize::new(1).unwrap()),
             Type::U16 | Type::S16 => Alignment::Bytes(NonZeroUsize::new(2).unwrap()),
-            Type::U32 | Type::S32 | Type::F32 | Type::Char => {
+            Type::U32 | Type::S32 | Type::F32 | Type::Char | Type::ErrorContext => {
                 Alignment::Bytes(NonZeroUsize::new(4).unwrap())
             }
             Type::U64 | Type::S64 | Type::F64 => Alignment::Bytes(NonZeroUsize::new(8).unwrap()),


Test patch:
diff --git a/crates/wit-parser/tests/ui/error-context.wit.json b/crates/wit-parser/tests/ui/error-context.wit.json
index 555a2543..617e37d1 100644
--- a/crates/wit-parser/tests/ui/error-context.wit.json
+++ b/crates/wit-parser/tests/ui/error-context.wit.json
@@ -13,14 +13,14 @@
           "params": [
             {
               "name": "x",
-              "type": 1
+              "type": "error-context"
             },
             {
               "name": "y",
               "type": 0
             }
           ],
-          "result": 2
+          "result": 1
         }
       },
       "package": 0
@@ -29,22 +29,19 @@
   "types": [
     {
       "name": "t1",
-      "kind": "error-context",
+      "kind": {
+        "type": "error-context"
+      },
       "owner": {
         "interface": 0
       }
     },
-    {
-      "name": null,
-      "kind": "error-context",
-      "owner": null
-    },
     {
       "name": null,
       "kind": {
         "result": {
           "ok": null,
-          "err": 1
+          "err": "error-context"
         }
       },
       "owner": null