Split the ComponentValue trait into... components (#4236)

This commit splits the current `ComponentValue` trait into three
separate traits:

* `ComponentType` - contains size/align/typecheck information in
  addition to the "lower" representation.
* `Lift` - only contains `lift` and `load`
* `Lower` - only contains `lower` and `store`

When describing the original implementation of host functions to Nick he
immediately pointed out this superior solution to the traits involved
with Wasmtime's support for typed parameters/returns in exported and
imported functions. Instead of having dynamic errors at runtime for
things like "you can't lift a `String`" that's instead a static
compile-time error now.

While I was doing this split I also refactored the `ComponentParams`
trait a bit to have `ComponentType` as a supertrait instead of a subtype
which made its implementations a bit more compact. Additionally its impl
blocks were folded into the existing tuple impl blocks.
This commit is contained in:
Alex Crichton
2022-06-07 12:29:26 -05:00
committed by GitHub
parent 511c53703a
commit 11ff9650e5
6 changed files with 348 additions and 422 deletions

View File

@@ -196,8 +196,8 @@ impl Func {
/// ``` /// ```
pub fn typed<Params, Return, S>(&self, store: S) -> Result<TypedFunc<Params, Return>> pub fn typed<Params, Return, S>(&self, store: S) -> Result<TypedFunc<Params, Return>>
where where
Params: ComponentParams, Params: ComponentParams + Lower,
Return: ComponentValue, Return: Lift,
S: AsContext, S: AsContext,
{ {
self.typecheck::<Params, Return>(store.as_context().0)?; self.typecheck::<Params, Return>(store.as_context().0)?;
@@ -206,16 +206,15 @@ impl Func {
fn typecheck<Params, Return>(&self, store: &StoreOpaque) -> Result<()> fn typecheck<Params, Return>(&self, store: &StoreOpaque) -> Result<()>
where where
Params: ComponentParams, Params: ComponentParams + Lower,
Return: ComponentValue, Return: Lift,
{ {
let data = &store[self.0]; let data = &store[self.0];
let ty = &data.types[data.ty]; let ty = &data.types[data.ty];
Params::typecheck(&ty.params, &data.types, Op::Lower) Params::typecheck_params(&ty.params, &data.types)
.context("type mismatch with parameters")?; .context("type mismatch with parameters")?;
Return::typecheck(&ty.result, &data.types, Op::Lift) Return::typecheck(&ty.result, &data.types).context("type mismatch with result")?;
.context("type mismatch with result")?;
Ok(()) Ok(())
} }

View File

@@ -1,5 +1,5 @@
use crate::component::func::{MAX_STACK_PARAMS, MAX_STACK_RESULTS}; use crate::component::func::{MAX_STACK_PARAMS, MAX_STACK_RESULTS};
use crate::component::{ComponentParams, ComponentValue, Memory, MemoryMut, Op, Options}; use crate::component::{ComponentParams, ComponentType, Lift, Lower, Memory, MemoryMut, Options};
use crate::{AsContextMut, StoreContextMut, ValRaw}; use crate::{AsContextMut, StoreContextMut, ValRaw};
use anyhow::{bail, Context, Result}; use anyhow::{bail, Context, Result};
use std::any::Any; use std::any::Any;
@@ -46,8 +46,8 @@ impl HostFunc {
fn new<F, P, R>(func: F, entrypoint: VMLoweringCallee) -> Arc<HostFunc> fn new<F, P, R>(func: F, entrypoint: VMLoweringCallee) -> Arc<HostFunc>
where where
F: Send + Sync + 'static, F: Send + Sync + 'static,
P: ComponentParams, P: ComponentParams + Lift,
R: ComponentValue, R: Lower,
{ {
Arc::new(HostFunc { Arc::new(HostFunc {
entrypoint, entrypoint,
@@ -71,12 +71,12 @@ impl HostFunc {
fn typecheck<P, R>(ty: FuncTypeIndex, types: &ComponentTypes) -> Result<()> fn typecheck<P, R>(ty: FuncTypeIndex, types: &ComponentTypes) -> Result<()>
where where
P: ComponentParams, P: ComponentParams + Lift,
R: ComponentValue, R: Lower,
{ {
let ty = &types[ty]; let ty = &types[ty];
P::typecheck(&ty.params, types, Op::Lift).context("type mismatch with parameters")?; P::typecheck_params(&ty.params, types).context("type mismatch with parameters")?;
R::typecheck(&ty.result, types, Op::Lower).context("type mismatch with result")?; R::typecheck(&ty.result, types).context("type mismatch with result")?;
Ok(()) Ok(())
} }
@@ -110,8 +110,8 @@ unsafe fn call_host<T, Params, Return, F>(
closure: F, closure: F,
) -> Result<()> ) -> Result<()>
where where
Params: ComponentValue, Params: Lift,
Return: ComponentValue, Return: Lower,
F: FnOnce(StoreContextMut<'_, T>, Params) -> Result<Return>, F: FnOnce(StoreContextMut<'_, T>, Params) -> Result<Return>,
{ {
/// Representation of arguments to this function when a return pointer is in /// Representation of arguments to this function when a return pointer is in
@@ -227,7 +227,7 @@ where
} }
} }
fn validate_inbounds<T: ComponentValue>(memory: &[u8], ptr: &ValRaw) -> Result<usize> { fn validate_inbounds<T: ComponentType>(memory: &[u8], ptr: &ValRaw) -> Result<usize> {
// FIXME: needs memory64 support // FIXME: needs memory64 support
let ptr = usize::try_from(ptr.get_u32())?; let ptr = usize::try_from(ptr.get_u32())?;
let end = match ptr.checked_add(T::size()) { let end = match ptr.checked_add(T::size()) {
@@ -271,8 +271,8 @@ macro_rules! impl_into_component_func {
impl<T, F, $($args,)* R> IntoComponentFunc<T, ($($args,)*), R> for F impl<T, F, $($args,)* R> IntoComponentFunc<T, ($($args,)*), R> for F
where where
F: Fn($($args),*) -> Result<R> + Send + Sync + 'static, F: Fn($($args),*) -> Result<R> + Send + Sync + 'static,
($($args,)*): ComponentParams + ComponentValue, ($($args,)*): ComponentParams + Lift,
R: ComponentValue, R: Lower,
{ {
extern "C" fn entrypoint( extern "C" fn entrypoint(
cx: *mut VMOpaqueContext, cx: *mut VMOpaqueContext,
@@ -307,8 +307,8 @@ macro_rules! impl_into_component_func {
impl<T, F, $($args,)* R> IntoComponentFunc<T, (StoreContextMut<'_, T>, $($args,)*), R> for F impl<T, F, $($args,)* R> IntoComponentFunc<T, (StoreContextMut<'_, T>, $($args,)*), R> for F
where where
F: Fn(StoreContextMut<'_, T>, $($args),*) -> Result<R> + Send + Sync + 'static, F: Fn(StoreContextMut<'_, T>, $($args),*) -> Result<R> + Send + Sync + 'static,
($($args,)*): ComponentParams + ComponentValue, ($($args,)*): ComponentParams + Lift,
R: ComponentValue, R: Lower,
{ {
extern "C" fn entrypoint( extern "C" fn entrypoint(
cx: *mut VMOpaqueContext, cx: *mut VMOpaqueContext,

File diff suppressed because it is too large Load Diff

View File

@@ -1,5 +1,5 @@
use crate::component::func::HostFunc; use crate::component::func::HostFunc;
use crate::component::{Component, ComponentParams, ComponentValue, Func, TypedFunc}; use crate::component::{Component, ComponentParams, Func, Lift, Lower, TypedFunc};
use crate::instance::OwnedImports; use crate::instance::OwnedImports;
use crate::store::{StoreOpaque, Stored}; use crate::store::{StoreOpaque, Stored};
use crate::{AsContextMut, Module, StoreContext, StoreContextMut}; use crate::{AsContextMut, Module, StoreContext, StoreContextMut};
@@ -86,8 +86,8 @@ impl Instance {
name: &str, name: &str,
) -> Result<TypedFunc<Params, Results>> ) -> Result<TypedFunc<Params, Results>>
where where
Params: ComponentParams, Params: ComponentParams + Lower,
Results: ComponentValue, Results: Lift,
S: AsContextMut, S: AsContextMut,
{ {
let f = self let f = self

View File

@@ -11,7 +11,8 @@ mod matching;
mod store; mod store;
pub use self::component::Component; pub use self::component::Component;
pub use self::func::{ pub use self::func::{
ComponentParams, ComponentValue, Func, IntoComponentFunc, Op, TypedFunc, WasmList, WasmStr, ComponentParams, ComponentType, Func, IntoComponentFunc, Lift, Lower, TypedFunc, WasmList,
WasmStr,
}; };
pub use self::instance::{Instance, InstancePre}; pub use self::instance::{Instance, InstancePre};
pub use self::linker::Linker; pub use self::linker::Linker;

View File

@@ -158,10 +158,6 @@ fn typecheck() -> Result<()> {
assert!(thunk.typed::<(u32,), (), _>(&store).is_err()); assert!(thunk.typed::<(u32,), (), _>(&store).is_err());
assert!(thunk.typed::<(), (), _>(&store).is_ok()); assert!(thunk.typed::<(), (), _>(&store).is_ok());
assert!(take_string.typed::<(), (), _>(&store).is_err()); assert!(take_string.typed::<(), (), _>(&store).is_err());
assert!(take_string.typed::<(), String, _>(&store).is_err());
assert!(take_string
.typed::<(String, String), String, _>(&store)
.is_err());
assert!(take_string.typed::<(String,), (), _>(&store).is_ok()); assert!(take_string.typed::<(String,), (), _>(&store).is_ok());
assert!(take_string.typed::<(&str,), (), _>(&store).is_ok()); assert!(take_string.typed::<(&str,), (), _>(&store).is_ok());
assert!(take_string.typed::<(&[u8],), (), _>(&store).is_err()); assert!(take_string.typed::<(&[u8],), (), _>(&store).is_err());
@@ -175,11 +171,7 @@ fn typecheck() -> Result<()> {
assert!(ret_tuple1.typed::<(), (u32,), _>(&store).is_ok()); assert!(ret_tuple1.typed::<(), (u32,), _>(&store).is_ok());
assert!(ret_tuple1.typed::<(), u32, _>(&store).is_err()); assert!(ret_tuple1.typed::<(), u32, _>(&store).is_err());
assert!(ret_string.typed::<(), (), _>(&store).is_err()); assert!(ret_string.typed::<(), (), _>(&store).is_err());
assert!(ret_string.typed::<(), String, _>(&store).is_err());
assert!(ret_string.typed::<(), &str, _>(&store).is_err());
assert!(ret_string.typed::<(), WasmStr, _>(&store).is_ok()); assert!(ret_string.typed::<(), WasmStr, _>(&store).is_ok());
assert!(ret_list_u8.typed::<(), &[u8], _>(&store).is_err());
assert!(ret_list_u8.typed::<(), Vec<u8>, _>(&store).is_err());
assert!(ret_list_u8.typed::<(), WasmList<u16>, _>(&store).is_err()); assert!(ret_list_u8.typed::<(), WasmList<u16>, _>(&store).is_err());
assert!(ret_list_u8.typed::<(), WasmList<i8>, _>(&store).is_err()); assert!(ret_list_u8.typed::<(), WasmList<i8>, _>(&store).is_err());
assert!(ret_list_u8.typed::<(), WasmList<u8>, _>(&store).is_ok()); assert!(ret_list_u8.typed::<(), WasmList<u8>, _>(&store).is_ok());