From e0e94e3b35674b66ce1b98dad99ae77fc0a1a404 Mon Sep 17 00:00:00 2001 From: Joel Dice Date: Thu, 19 Dec 2024 11:02:13 -0700 Subject: [PATCH] support streams, etc. in WitPrinter::declare_type (#1959) I had originally thought that code was unreachable for such types, but `wit-smith` taught me otherwise. add stream/future/error-context to wit-smith I had to tweak fuzz/src/wit64.rs because otherwise the old version of `wit-component` it was using choked on these new types. Now we just return early when that happens. Signed-off-by: Joel Dice --- crates/wit-component/src/printing.rs | 68 ++++++++++++++++++++++++++-- crates/wit-smith/src/generate.rs | 28 ++++++++++++ fuzz/src/wit64.rs | 7 ++- 3 files changed, 99 insertions(+), 4 deletions(-) diff --git a/crates/wit-component/src/printing.rs b/crates/wit-component/src/printing.rs index bbd55d3c8e..5ea9397739 100644 --- a/crates/wit-component/src/printing.rs +++ b/crates/wit-component/src/printing.rs @@ -754,9 +754,13 @@ impl WitPrinter { } None => bail!("unnamed type in document"), }, - TypeDefKind::Future(_) => panic!("no need to declare futures"), - TypeDefKind::Stream(_) => panic!("no need to declare streams"), - TypeDefKind::ErrorContext => panic!("no need to declare error-contexts"), + TypeDefKind::Future(inner) => { + self.declare_future(resolve, ty.name.as_deref(), inner.as_ref())? + } + TypeDefKind::Stream(inner) => { + self.declare_stream(resolve, ty.name.as_deref(), inner)? + } + TypeDefKind::ErrorContext => self.declare_error_context(ty.name.as_deref())?, TypeDefKind::Unknown => unreachable!(), } } @@ -953,6 +957,58 @@ impl WitPrinter { Ok(()) } + fn declare_stream(&mut self, resolve: &Resolve, name: Option<&str>, ty: &Type) -> Result<()> { + if let Some(name) = name { + self.output.keyword("type"); + self.output.str(" "); + self.print_name_type(name, TypeKind::Stream); + self.output.str(" = "); + self.output.ty("stream", TypeKind::BuiltIn); + self.output.str("<"); + self.print_type_name(resolve, ty)?; + self.output.str(">"); + self.output.semicolon(); + } + + Ok(()) + } + + fn declare_future( + &mut self, + resolve: &Resolve, + name: Option<&str>, + ty: Option<&Type>, + ) -> Result<()> { + if let Some(name) = name { + self.output.keyword("type"); + self.output.str(" "); + self.print_name_type(name, TypeKind::Future); + self.output.str(" = "); + self.output.ty("future", TypeKind::BuiltIn); + if let Some(ty) = ty { + self.output.str("<"); + self.print_type_name(resolve, ty)?; + self.output.str(">"); + } + self.output.semicolon(); + } + + 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 { if is_keyword(name) { Cow::Owned(format!("%{name}")) @@ -1212,6 +1268,8 @@ pub enum TypeKind { BuiltIn, /// An enumeration type name. Enum, + /// An error-context type name. + ErrorContext, /// A flags type name. Flags, /// A freestanding function name, not associated with any specific type or namespace. @@ -1221,6 +1279,8 @@ pub enum TypeKind { FunctionMethod, /// A static function, associated with a resource. FunctionStatic, + /// A future type name. + Future, /// An interface declaration name. InterfaceDeclaration, /// An interface name when printing a path, for example in `use`. @@ -1243,6 +1303,8 @@ pub enum TypeKind { Resource, /// A result type name. Result, + /// A stream type name. + Stream, /// A tuple type name. Tuple, /// A type alias. diff --git a/crates/wit-smith/src/generate.rs b/crates/wit-smith/src/generate.rs index c6d5d49fbe..cc439754ad 100644 --- a/crates/wit-smith/src/generate.rs +++ b/crates/wit-smith/src/generate.rs @@ -716,6 +716,9 @@ impl<'a> InterfaceGenerator<'a> { Option, Result, List, + Stream, + Future, + ErrorContext, } *fuel = match fuel.checked_sub(1) { @@ -823,6 +826,31 @@ impl<'a> InterfaceGenerator<'a> { (false, false) => {} } } + Kind::Stream => { + *fuel = match fuel.checked_sub(1) { + Some(fuel) => fuel, + None => continue, + }; + dst.push_str("stream<"); + self.gen_type(u, fuel, dst)?; + dst.push_str(">"); + } + Kind::Future => { + *fuel = match fuel.checked_sub(1) { + Some(fuel) => fuel, + None => continue, + }; + if u.arbitrary()? { + dst.push_str("future<"); + self.gen_type(u, fuel, dst)?; + dst.push_str(">"); + } else { + dst.push_str("future"); + } + } + Kind::ErrorContext => { + dst.push_str("error-context"); + } }; } diff --git a/fuzz/src/wit64.rs b/fuzz/src/wit64.rs index b6e8af8c07..6060b754ee 100644 --- a/fuzz/src/wit64.rs +++ b/fuzz/src/wit64.rs @@ -9,7 +9,12 @@ pub fn run(u: &mut Unstructured<'_>) -> Result<()> { wit_smith::smith(&config, u) })?; write_file("doc.wasm", &wasm); - let r1 = wit_component_old::decode(&wasm).unwrap(); + let Ok(r1) = wit_component_old::decode(&wasm) else { + // Presumably this is because the old version of `wit-component` doesn't + // understand the new `stream`, `future`, or `error-context` types, in + // which case there's no point in continuing, so we just return early. + return Ok(()); + }; let r1 = r1.resolve(); let r2 = wit_component_new::decode(&wasm).unwrap(); let r2 = r2.resolve();