Repository: bytecodealliance/wasmtime
Pull request #7792 (2024-01-18 09:03:13)

Make wasmtime dependency in wiggle and wasi-common optional with a default feature
<!--
Please make sure you include the following information:

- If this work has been discussed elsewhere, please include a link to that
  conversation. If it was discussed in an issue, just mention "issue #...".

- Explain why this change is needed. If the details are in an issue already,
  this can be brief.

Our development process is documented in the Wasmtime book:
https://docs.wasmtime.dev/contributing-development-process.html

Please ensure all communication follows the code of conduct:
https://github.com/bytecodealliance/wasmtime/blob/main/CODE_OF_CONDUCT.md
-->

I am trying to use `wasi-common` (and thus also `wiggle`) to provide a common `WasiCtx` implementation that is reused between `wasmtime` and a custom runtime (as briefly mentioned in https://github.com/bytecodealliance/wasmtime/issues/7776). At the moment, compiling these packages also compiles `wasmtime`, which is not supported on my platform. I propose to add an enabled-by-default feature to `wasi-common` and `wiggle` that guards all `wasmtime`-specific code and allows both packages to be compiled without a dependency on `wasmtime` using `default-features = false`.


Patch:
diff --git a/Cargo.toml b/Cargo.toml
index b7c83c71a..c66ed0db4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -179,7 +179,7 @@ component-fuzz-util = { path = "crates/misc/component-fuzz-util" }
 wiggle = { path = "crates/wiggle", version = "=18.0.0", default-features = false }
 wiggle-macro = { path = "crates/wiggle/macro", version = "=18.0.0" }
 wiggle-generate = { path = "crates/wiggle/generate", version = "=18.0.0" }
-wasi-common = { path = "crates/wasi-common", version = "=18.0.0" }
+wasi-common = { path = "crates/wasi-common", version = "=18.0.0", default-features = false }
 wasi-tokio = { path = "crates/wasi-common/tokio", version = "=18.0.0" }
 wasi-cap-std-sync = { path = "crates/wasi-common/cap-std-sync", version = "=18.0.0" }
 wasmtime-fuzzing = { path = "crates/fuzzing" }
diff --git a/crates/wasi-common/Cargo.toml b/crates/wasi-common/Cargo.toml
index 8437fbc50..c0479b9be 100644
--- a/crates/wasi-common/Cargo.toml
+++ b/crates/wasi-common/Cargo.toml
@@ -24,7 +24,7 @@ workspace = true
 anyhow = { workspace = true }
 thiserror = { workspace = true }
 wiggle = { workspace = true }
-wasmtime = { workspace = true }
+wasmtime = { workspace = true, optional = true }
 tracing = { workspace = true }
 cap-std = { workspace = true }
 cap-rand = { workspace = true }
@@ -54,7 +54,7 @@ test-programs-artifacts = { workspace = true }
 tokio = { workspace = true, features = ['macros', 'rt-multi-thread'] }
 
 [features]
-default = ["trace_log"]
+default = ["trace_log", "wasmtime"]
 # This feature enables the `tracing` logs in the calls to target the `log`
 # ecosystem of backends (e.g. `env_logger`. Disable this if you want to use
 # `tracing-subscriber`.
@@ -62,3 +62,5 @@ trace_log = [ "wiggle/tracing_log", "tracing/log" ]
 # Need to make the wiggle_metadata feature available to consumers of this
 # crate if they want the snapshots to have metadata available.
 wiggle_metadata = ["wiggle/wiggle_metadata"]
+# This feature enables integration with wasmtime.
+wasmtime = ["dep:wasmtime", "wiggle/wasmtime"]
\ No newline at end of file
diff --git a/crates/wasi/Cargo.toml b/crates/wasi/Cargo.toml
index f75bc8532..33fc29cc0 100644
--- a/crates/wasi/Cargo.toml
+++ b/crates/wasi/Cargo.toml
@@ -20,7 +20,7 @@ anyhow = { workspace = true }
 wasi-common = { workspace = true }
 wasi-cap-std-sync = { workspace = true, optional = true }
 wasi-tokio = { workspace = true, optional = true }
-wiggle = { workspace = true, optional = true }
+wiggle = { workspace = true, optional = true, features = ["wasmtime"] }
 libc = { workspace = true }
 once_cell = { workspace = true }
 log = { workspace = true }
diff --git a/crates/wasmtime/src/component/func.rs b/crates/wasmtime/src/component/func.rs
index 0b69aff34..cf4f710f1 100644
--- a/crates/wasmtime/src/component/func.rs
+++ b/crates/wasmtime/src/component/func.rs
@@ -362,7 +362,8 @@ impl Func {
         }
 
         for (param, ty) in params.iter().zip(param_tys.iter()) {
-            ty.check(param).context("type mismatch with parameters")?;
+            ty.is_supertype_of(param)
+                .context("type mismatch with parameters")?;
         }
 
         self.call_raw(
diff --git a/crates/wasmtime/src/component/func/host.rs b/crates/wasmtime/src/component/func/host.rs
index a048275b9..c00f38c83 100644
--- a/crates/wasmtime/src/component/func/host.rs
+++ b/crates/wasmtime/src/component/func/host.rs
@@ -385,7 +385,7 @@ where
     let mut cx = LowerContext::new(store, &options, types, instance);
     let instance = cx.instance_type();
     for (val, ty) in result_vals.iter().zip(result_tys.types.iter()) {
-        Type::from(ty, &instance).check(val)?;
+        Type::from(ty, &instance).is_supertype_of(val)?;
     }
     if let Some(cnt) = result_tys.abi.flat_count(MAX_FLAT_RESULTS) {
         let mut dst = storage[..cnt].iter_mut();
diff --git a/crates/wasmtime/src/component/types.rs b/crates/wasmtime/src/component/types.rs
index f751079fa..401ba15ec 100644
--- a/crates/wasmtime/src/component/types.rs
+++ b/crates/wasmtime/src/component/types.rs
@@ -675,9 +675,16 @@ impl Type {
         }
     }
 
-    pub(crate) fn check(&self, value: &Val) -> Result<()> {
+    /// Checks whether type of `value` is a subtype of `self`.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error in case of a type mismatch
+    pub(crate) fn is_supertype_of(&self, value: &Val) -> Result<()> {
         let other = &value.ty();
-        if self == other {
+        if self == other
+            || matches!((self, other), (Self::Borrow(s), Self::Own(other)) if s == other)
+        {
             Ok(())
         } else if mem::discriminant(self) != mem::discriminant(other) {
             Err(anyhow!(
diff --git a/crates/wasmtime/src/component/values.rs b/crates/wasmtime/src/component/values.rs
index 332a0659a..0775dcf7b 100644
--- a/crates/wasmtime/src/component/values.rs
+++ b/crates/wasmtime/src/component/values.rs
@@ -26,7 +26,7 @@ impl List {
         let element_type = ty.ty();
         for (index, value) in values.iter().enumerate() {
             element_type
-                .check(value)
+                .is_supertype_of(value)
                 .with_context(|| format!("type mismatch for element {index} of list"))?;
         }
 
@@ -83,7 +83,7 @@ impl Record {
                     if name == field.name {
                         field
                             .ty
-                            .check(&value)
+                            .is_supertype_of(&value)
                             .with_context(|| format!("type mismatch for field {name} of record"))?;
 
                         values.push(value);
@@ -150,7 +150,7 @@ impl Tuple {
         }
 
         for (index, (value, ty)) in values.iter().zip(ty.types()).enumerate() {
-            ty.check(value)
+            ty.is_supertype_of(value)
                 .with_context(|| format!("type mismatch for field {index} of tuple"))?;
         }
 
@@ -256,7 +256,7 @@ impl Variant {
 fn typecheck_payload(name: &str, case_type: Option<&Type>, value: Option<&Val>) -> Result<()> {
     match (case_type, value) {
         (Some(expected), Some(actual)) => expected
-            .check(&actual)
+            .is_supertype_of(&actual)
             .with_context(|| format!("type mismatch for case {name} of variant")),
         (None, None) => Ok(()),
         (Some(_), None) => bail!("expected a payload for case `{name}`"),
@@ -341,7 +341,9 @@ impl OptionVal {
     pub fn new(ty: &types::OptionType, value: Option<Val>) -> Result<Self> {
         let value = value
             .map(|value| {
-                ty.ty().check(&value).context("type mismatch for option")?;
+                ty.ty()
+                    .is_supertype_of(&value)
+                    .context("type mismatch for option")?;
 
                 Ok::<_, Error>(value)
             })
diff --git a/crates/wiggle/Cargo.toml b/crates/wiggle/Cargo.toml
index b1d65de00..7dfa3a9a2 100644
--- a/crates/wiggle/Cargo.toml
+++ b/crates/wiggle/Cargo.toml
@@ -20,13 +20,14 @@ wiggle-macro = { workspace = true }
 tracing = { workspace = true }
 bitflags = { workspace = true }
 async-trait = { workspace = true }
-wasmtime = { workspace = true }
+wasmtime = { workspace = true, optional = true }
 anyhow = { workspace = true }
 
 [dev-dependencies]
 wiggle-test = { path = "test-helpers" }
 proptest = "1.0.0"
 tokio = { version = "1", features = ["rt-multi-thread","time", "macros"] }
+wasmtime = { workspace = true }
 
 [[test]]
 name = "atoms_async"
@@ -62,7 +63,10 @@ wiggle_metadata = ['witx', "wiggle-macro/wiggle_metadata"]
 # the logs out of wiggle-generated libraries.
 tracing_log = [ "tracing/log" ]
 
+# This feature enables integration with wasmtime.
+wasmtime = ["dep:wasmtime"]
+
 # Support for async in the wasmtime crates.
 wasmtime_async = [ "wasmtime/async" ]
 
-default = ["wiggle_metadata", "wasmtime_async" ]
+default = ["wiggle_metadata", "wasmtime", "wasmtime_async" ]
diff --git a/crates/wiggle/src/lib.rs b/crates/wiggle/src/lib.rs
index 5fe7eb124..14141c36d 100644
--- a/crates/wiggle/src/lib.rs
+++ b/crates/wiggle/src/lib.rs
@@ -32,6 +32,7 @@ pub mod async_trait_crate {
 }
 
 pub mod wasmtime;
+#[cfg(feature = "wasmtime")]
 pub mod wasmtime_crate {
     pub use wasmtime::*;
 }


Test patch:
diff --git a/tests/all/component_model/resources.rs b/tests/all/component_model/resources.rs
index 448eceecf..bb5938944 100644
--- a/tests/all/component_model/resources.rs
+++ b/tests/all/component_model/resources.rs
@@ -856,6 +856,72 @@ fn cannot_use_borrow_for_own() -> Result<()> {
     Ok(())
 }
 
+#[test]
+fn can_use_own_for_borrow() -> Result<()> {
+    let engine = super::engine();
+    let c = Component::new(
+        &engine,
+        r#"
+            (component
+                (import "t" (type $t (sub resource)))
+
+                (core func $drop (canon resource.drop $t))
+
+                (core module $m
+                    (import "" "drop" (func $drop (param i32)))
+                    (func (export "f") (param i32)
+                        (call $drop (local.get 0))
+                    )
+                )
+                (core instance $i (instantiate $m
+                    (with "" (instance
+                        (export "drop" (func $drop))
+                    ))
+                ))
+
+                (func (export "f") (param "x" (borrow $t))
+                    (canon lift (core func $i "f")))
+            )
+        "#,
+    )?;
+
+    struct MyType;
+
+    let mut store = Store::new(&engine, ());
+    let mut linker = Linker::new(&engine);
+    let ty_idx = linker
+        .root()
+        .resource("t", ResourceType::host::<MyType>(), |_, _| Ok(()))?;
+    let i_pre = linker.instantiate_pre(&c)?;
+    let i = i_pre.instantiate(&mut store)?;
+
+    let f = i.get_func(&mut store, "f").unwrap();
+    let f_typed = i.get_typed_func::<(&Resource<MyType>,), ()>(&mut store, "f")?;
+
+    let resource = Resource::new_own(100);
+    f_typed.call(&mut store, (&resource,))?;
+    f_typed.post_return(&mut store)?;
+
+    let resource = Resource::new_borrow(200);
+    f_typed.call(&mut store, (&resource,))?;
+    f_typed.post_return(&mut store)?;
+
+    let resource =
+        Resource::<MyType>::new_own(300).try_into_resource_any(&mut store, &i_pre, ty_idx)?;
+    f.call(&mut store, &[Val::Resource(resource)], &mut [])?;
+    f.post_return(&mut store)?;
+    resource.resource_drop(&mut store)?;
+
+    // TODO: Enable once https://github.com/bytecodealliance/wasmtime/issues/7793 is fixed
+    //let resource =
+    //    Resource::<MyType>::new_borrow(400).try_into_resource_any(&mut store, &i_pre, ty_idx)?;
+    //f.call(&mut store, &[Val::Resource(resource)], &mut [])?;
+    //f.post_return(&mut store)?;
+    //resource.resource_drop(&mut store)?;
+
+    Ok(())
+}
+
 #[test]
 fn passthrough_wrong_type() -> Result<()> {
     let engine = super::engine();