Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixed length list support #1992

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 23 additions & 1 deletion crates/wasm-compose/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -649,6 +649,9 @@ impl<'a> TypeEncoder<'a> {
ComponentDefinedType::Record(r) => self.record(state, r),
ComponentDefinedType::Variant(v) => self.variant(state, v),
ComponentDefinedType::List(ty) => self.list(state, *ty),
ComponentDefinedType::FixedSizeList(ty, elements) => {
self.fixed_size_list(state, *ty, *elements)
}
ComponentDefinedType::Tuple(t) => self.tuple(state, t),
ComponentDefinedType::Flags(names) => Self::flags(&mut state.cur.encodable, names),
ComponentDefinedType::Enum(cases) => Self::enum_type(&mut state.cur.encodable, cases),
Expand Down Expand Up @@ -709,6 +712,23 @@ impl<'a> TypeEncoder<'a> {
index
}

fn fixed_size_list(
&self,
state: &mut TypeState<'a>,
ty: ct::ComponentValType,
elements: u32,
) -> u32 {
let ty = self.component_val_type(state, ty);
let index = state.cur.encodable.type_count();
state
.cur
.encodable
.ty()
.defined_type()
.fixed_size_list(ty, elements);
index
}

fn tuple(&self, state: &mut TypeState<'a>, tuple: &TupleType) -> u32 {
let types = tuple
.types
Expand Down Expand Up @@ -1228,7 +1248,9 @@ impl DependencyRegistrar<'_, '_> {
ComponentDefinedType::Primitive(_)
| ComponentDefinedType::Enum(_)
| ComponentDefinedType::Flags(_) => {}
ComponentDefinedType::List(t) | ComponentDefinedType::Option(t) => self.val_type(*t),
ComponentDefinedType::List(t)
| ComponentDefinedType::FixedSizeList(t, _)
| ComponentDefinedType::Option(t) => self.val_type(*t),
ComponentDefinedType::Own(r) | ComponentDefinedType::Borrow(r) => {
self.ty(ComponentAnyTypeId::Resource(*r))
}
Expand Down
7 changes: 7 additions & 0 deletions crates/wasm-encoder/src/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -593,6 +593,13 @@ impl ComponentDefinedTypeEncoder<'_> {
ty.into().encode(self.0);
}

/// Define a fixed size list type.
pub fn fixed_size_list(self, ty: impl Into<ComponentValType>, elements: u32) {
self.0.push(0x67);
ty.into().encode(self.0);
elements.encode(self.0);
}

/// Define a tuple type.
pub fn tuple<I, T>(self, types: I)
where
Expand Down
3 changes: 3 additions & 0 deletions crates/wasm-encoder/src/reencode/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -767,6 +767,9 @@ pub mod component_utils {
wasmparser::ComponentDefinedType::List(t) => {
defined.list(reencoder.component_val_type(t));
}
wasmparser::ComponentDefinedType::FixedSizeList(t, elements) => {
defined.fixed_size_list(reencoder.component_val_type(t), elements);
}
wasmparser::ComponentDefinedType::Tuple(t) => {
defined.tuple(t.iter().map(|t| reencoder.component_val_type(*t)));
}
Expand Down
11 changes: 11 additions & 0 deletions crates/wasm-wave/src/value/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ pub struct Type(pub(super) TypeEnum);
pub(super) enum TypeEnum {
Simple(SimpleType),
List(Arc<ListType>),
FixedSizeList(Arc<ListType>, u32),
Record(Arc<RecordType>),
Tuple(Arc<TupleType>),
Variant(Arc<VariantType>),
Expand Down Expand Up @@ -55,6 +56,15 @@ impl Type {
Self(TypeEnum::List(Arc::new(ListType { element })))
}

/// Returns a list type with the given element type.
pub fn fixed_size_list(element_type: impl Into<Self>, elements: u32) -> Self {
let element = element_type.into();
Self(TypeEnum::FixedSizeList(
Arc::new(ListType { element }),
elements,
))
}

/// Returns a record type with the given field types. Returns None if
/// `fields` is empty.
pub fn record<T: Into<Box<str>>>(
Expand Down Expand Up @@ -189,6 +199,7 @@ impl WasmType for Type {
match self.0 {
TypeEnum::Simple(simple) => simple.0,
TypeEnum::List(_) => WasmTypeKind::List,
TypeEnum::FixedSizeList(_, _) => WasmTypeKind::FixedSizeList,
TypeEnum::Record(_) => WasmTypeKind::Record,
TypeEnum::Tuple(_) => WasmTypeKind::Tuple,
TypeEnum::Variant(_) => WasmTypeKind::Variant,
Expand Down
8 changes: 8 additions & 0 deletions crates/wasm-wave/src/value/wit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ impl<'a> TypeResolver<'a> {
TypeDefKind::Option(some_type) => self.resolve_option(some_type),
TypeDefKind::Result(result) => self.resolve_result(result),
TypeDefKind::List(element_type) => self.resolve_list(element_type),
TypeDefKind::FixedSizeList(element_type, elements) => {
self.resolve_fixed_size_list(element_type, *elements)
}
TypeDefKind::Type(Type::Bool) => Ok(value::Type::BOOL),
TypeDefKind::Type(Type::U8) => Ok(value::Type::U8),
TypeDefKind::Type(Type::U16) => Ok(value::Type::U16),
Expand Down Expand Up @@ -145,6 +148,11 @@ impl<'a> TypeResolver<'a> {
let element_type = self.resolve_type(*element_type)?;
Ok(value::Type::list(element_type))
}

fn resolve_fixed_size_list(&self, element_type: &Type, elements: u32) -> ValueResult {
let element_type = self.resolve_type(*element_type)?;
Ok(value::Type::fixed_size_list(element_type, elements))
}
}

#[cfg(test)]
Expand Down
2 changes: 2 additions & 0 deletions crates/wasm-wave/src/wasm/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub enum WasmTypeKind {
Char,
String,
List,
FixedSizeList,
Record,
Tuple,
Variant,
Expand Down Expand Up @@ -48,6 +49,7 @@ impl std::fmt::Display for WasmTypeKind {
WasmTypeKind::Char => "char",
WasmTypeKind::String => "string",
WasmTypeKind::List => "list",
WasmTypeKind::FixedSizeList => "list<_,N>",
WasmTypeKind::Record => "record",
WasmTypeKind::Tuple => "tuple",
WasmTypeKind::Variant => "variant",
Expand Down
10 changes: 10 additions & 0 deletions crates/wasm-wave/src/writer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,16 @@ impl<W: Write> Writer<W> {
}
self.write_str("]")
}
WasmTypeKind::FixedSizeList => {
self.write_str("[")?;
for (idx, val) in val.unwrap_list().enumerate() {
if idx != 0 {
self.write_str(", ")?;
}
self.write_value(&*val)?;
}
self.write_str("]")
}
WasmTypeKind::Record => {
self.write_str("{")?;
let mut first = true;
Expand Down
5 changes: 5 additions & 0 deletions crates/wasmparser/src/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,11 @@ define_wasm_features! {
/// Corresponds to the 🚝 character in
/// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
pub cm_async_builtins: CM_ASYNC_BUILTINS(1 << 29) = false;
/// Support for fixed size lists
///
/// Corresponds to the 🔧 character in
/// <https://github.com/WebAssembly/component-model/blob/main/design/mvp/Explainer.md>.
pub cm_fixed_size_list: CM_FIXED_SIZE_LIST(1 << 30) = false;
}
}

Expand Down
5 changes: 4 additions & 1 deletion crates/wasmparser/src/readers/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,8 @@ pub enum ComponentDefinedType<'a> {
Variant(Box<[VariantCase<'a>]>),
/// The type is a list of the given value type.
List(ComponentValType),
/// The type is a fixed size list of the given value type.
FixedSizeList(ComponentValType, u32),
/// The type is a tuple of the given value types.
Tuple(Box<[ComponentValType]>),
/// The type is flags with the given names.
Expand Down Expand Up @@ -503,8 +505,9 @@ impl<'a> ComponentDefinedType<'a> {
},
0x69 => ComponentDefinedType::Own(reader.read()?),
0x68 => ComponentDefinedType::Borrow(reader.read()?),
0x65 => ComponentDefinedType::Future(reader.read()?),
0x67 => ComponentDefinedType::FixedSizeList(reader.read()?, reader.read_var_u32()?),
0x66 => ComponentDefinedType::Stream(reader.read()?),
0x65 => ComponentDefinedType::Future(reader.read()?),
x => return reader.invalid_leading_byte(x, "component defined type"),
})
}
Expand Down
18 changes: 15 additions & 3 deletions crates/wasmparser/src/validator/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -745,9 +745,9 @@ impl ComponentState {
.map(|t| types.type_named_valtype(t, set))
.unwrap_or(true)
}
ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
types.type_named_valtype(ty, set)
}
ComponentDefinedType::List(ty)
| ComponentDefinedType::FixedSizeList(ty, _)
| ComponentDefinedType::Option(ty) => types.type_named_valtype(ty, set),

// The resource referred to by own/borrow must be named.
ComponentDefinedType::Own(id) | ComponentDefinedType::Borrow(id) => {
Expand Down Expand Up @@ -3485,6 +3485,18 @@ impl ComponentState {
crate::ComponentDefinedType::List(ty) => Ok(ComponentDefinedType::List(
self.create_component_val_type(ty, offset)?,
)),
crate::ComponentDefinedType::FixedSizeList(ty, elements) => {
if !features.cm_fixed_size_list() {
bail!(
offset,
"Fixed size lists require the component model fixed size list feature"
)
}
Ok(ComponentDefinedType::FixedSizeList(
self.create_component_val_type(ty, offset)?,
elements,
))
}
crate::ComponentDefinedType::Tuple(tys) => {
self.create_tuple_type(tys.as_ref(), types, offset)
}
Expand Down
32 changes: 25 additions & 7 deletions crates/wasmparser/src/validator/component_types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1081,6 +1081,8 @@ pub enum ComponentDefinedType {
Variant(VariantType),
/// The type is a list.
List(ComponentValType),
/// The type is a fixed size list.
FixedSizeList(ComponentValType, u32),
/// The type is a tuple.
Tuple(TupleType),
/// The type is a set of flags.
Expand Down Expand Up @@ -1121,7 +1123,7 @@ impl TypeData for ComponentDefinedType {
Self::Record(r) => r.info,
Self::Variant(v) => v.info,
Self::Tuple(t) => t.info,
Self::List(ty) | Self::Option(ty) => ty.info(types),
Self::List(ty) | Self::FixedSizeList(ty, _) | Self::Option(ty) => ty.info(types),
Self::Result { ok, err } => {
let default = TypeInfo::new();
let mut info = ok.map(|ty| ty.type_info(types)).unwrap_or(default);
Expand Down Expand Up @@ -1150,7 +1152,7 @@ impl ComponentDefinedType {
| Self::Borrow(_)
| Self::Future(_)
| Self::Stream(_) => false,
Self::Option(ty) => ty.contains_ptr(types),
Self::Option(ty) | Self::FixedSizeList(ty, _) => ty.contains_ptr(types),
Self::Result { ok, err } => {
ok.map(|ty| ty.contains_ptr(types)).unwrap_or(false)
|| err.map(|ty| ty.contains_ptr(types)).unwrap_or(false)
Expand All @@ -1171,6 +1173,9 @@ impl ComponentDefinedType {
lowered_types,
),
Self::List(_) => lowered_types.push(ValType::I32) && lowered_types.push(ValType::I32),
Self::FixedSizeList(ty, length) => {
(0..(*length).min(8192)).all(|_n| ty.push_wasm_types(types, lowered_types))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit wary of this in that it's sort of just a constant sitting here. For example if this stays as-is it should be documented as to why min is used and why 8k is chosen. Otherwise though what I was envisioning was an update to the validator to gate the structure of types to require that they fit under the 8k limit.

}
Self::Tuple(t) => t
.types
.iter()
Expand Down Expand Up @@ -1245,6 +1250,7 @@ impl ComponentDefinedType {
ComponentDefinedType::Flags(_) => "flags",
ComponentDefinedType::Option(_) => "option",
ComponentDefinedType::List(_) => "list",
ComponentDefinedType::FixedSizeList(_, _) => "fixed size list",
ComponentDefinedType::Result { .. } => "result",
ComponentDefinedType::Own(_) => "own",
ComponentDefinedType::Borrow(_) => "borrow",
Expand Down Expand Up @@ -1982,7 +1988,9 @@ impl TypeAlloc {
}
}
}
ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
ComponentDefinedType::List(ty)
| ComponentDefinedType::FixedSizeList(ty, _)
| ComponentDefinedType::Option(ty) => {
self.free_variables_valtype(ty, set);
}
ComponentDefinedType::Result { ok, err } => {
Expand Down Expand Up @@ -2124,9 +2132,9 @@ impl TypeAlloc {
.map(|t| self.type_named_valtype(t, set))
.unwrap_or(true)
}
ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
self.type_named_valtype(ty, set)
}
ComponentDefinedType::List(ty)
| ComponentDefinedType::FixedSizeList(ty, _)
| ComponentDefinedType::Option(ty) => self.type_named_valtype(ty, set),

// own/borrow themselves don't have to be named, but the resource
// they refer to must be named.
Expand Down Expand Up @@ -2311,7 +2319,9 @@ where
}
}
}
ComponentDefinedType::List(ty) | ComponentDefinedType::Option(ty) => {
ComponentDefinedType::List(ty)
| ComponentDefinedType::FixedSizeList(ty, _)
| ComponentDefinedType::Option(ty) => {
any_changed |= self.remap_valtype(ty, map);
}
ComponentDefinedType::Result { ok, err } => {
Expand Down Expand Up @@ -3193,6 +3203,14 @@ impl<'a> SubtypeCx<'a> {
(Variant(_), b) => bail!(offset, "expected {}, found variant", b.desc()),
(List(a), List(b)) | (Option(a), Option(b)) => self.component_val_type(a, b, offset),
(List(_), b) => bail!(offset, "expected {}, found list", b.desc()),
(FixedSizeList(a, asize), FixedSizeList(b, bsize)) => {
if asize != bsize {
bail!(offset, "expected fixed size {bsize}, found size {asize}")
} else {
self.component_val_type(a, b, offset)
}
}
(FixedSizeList(_, _), b) => bail!(offset, "expected {}, found list", b.desc()),
(Option(_), b) => bail!(offset, "expected {}, found option", b.desc()),
(Tuple(a), Tuple(b)) => {
if a.types.len() != b.types.len() {
Expand Down
16 changes: 16 additions & 0 deletions crates/wasmprinter/src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,19 @@ impl Printer<'_, '_> {
Ok(())
}

pub(crate) fn print_fixed_size_list_type(
&mut self,
state: &State,
element_ty: &ComponentValType,
elements: u32,
) -> Result<()> {
self.start_group("list ")?;
self.print_component_val_type(state, element_ty)?;
self.result.write_str(&format!(" {elements}"))?;
self.end_group()?;
Ok(())
}

pub(crate) fn print_tuple_type(
&mut self,
state: &State,
Expand Down Expand Up @@ -264,6 +277,9 @@ impl Printer<'_, '_> {
ComponentDefinedType::Record(fields) => self.print_record_type(state, fields)?,
ComponentDefinedType::Variant(cases) => self.print_variant_type(state, cases)?,
ComponentDefinedType::List(ty) => self.print_list_type(state, ty)?,
ComponentDefinedType::FixedSizeList(ty, elements) => {
self.print_fixed_size_list_type(state, ty, *elements)?
}
ComponentDefinedType::Tuple(tys) => self.print_tuple_type(state, tys)?,
ComponentDefinedType::Flags(names) => self.print_flag_or_enum_type("flags", names)?,
ComponentDefinedType::Enum(cases) => self.print_flag_or_enum_type("enum", cases)?,
Expand Down
6 changes: 5 additions & 1 deletion crates/wast/src/component/binary.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,11 @@ fn encode_defined_type(encoder: ComponentDefinedTypeEncoder, ty: &ComponentDefin
}));
}
ComponentDefinedType::List(l) => {
encoder.list(l.element.as_ref());
if let Some(elements) = l.elements {
encoder.fixed_size_list(l.element.as_ref(), elements);
} else {
encoder.list(l.element.as_ref());
}
}
ComponentDefinedType::Tuple(t) => {
encoder.tuple(t.fields.iter());
Expand Down
7 changes: 6 additions & 1 deletion crates/wast/src/component/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -584,13 +584,18 @@ impl<'a> Parse<'a> for Refinement<'a> {
pub struct List<'a> {
/// The element type of the array.
pub element: Box<ComponentValType<'a>>,
/// Optional fixed size
pub elements: Option<u32>,
}

impl<'a> Parse<'a> for List<'a> {
fn parse(parser: Parser<'a>) -> Result<Self> {
parser.parse::<kw::list>()?;
let tp = parser.parse()?;
let elements = parser.parse()?;
Ok(Self {
element: Box::new(parser.parse()?),
element: Box::new(tp),
elements,
})
}
}
Expand Down
1 change: 1 addition & 0 deletions crates/wit-component/src/encoding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ impl TypeContents {
}
TypeDefKind::Enum(_) => Self::empty(),
TypeDefKind::List(t) => Self::for_type(resolve, t) | Self::LIST,
TypeDefKind::FixedSizeList(t, _elements) => Self::for_type(resolve, t),
TypeDefKind::Type(t) => Self::for_type(resolve, t),
TypeDefKind::Future(_) => Self::empty(),
TypeDefKind::Stream(_) => Self::empty(),
Expand Down
6 changes: 6 additions & 0 deletions crates/wit-component/src/encoding/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,12 @@ pub trait ValtypeEncoder<'a> {
encoder.list(ty);
ComponentValType::Type(index)
}
TypeDefKind::FixedSizeList(ty, elements) => {
let ty = self.encode_valtype(resolve, ty)?;
let (index, encoder) = self.defined_type();
encoder.fixed_size_list(ty, *elements);
ComponentValType::Type(index)
}
TypeDefKind::Type(ty) => self.encode_valtype(resolve, ty)?,
TypeDefKind::Future(ty) => self.encode_future(resolve, ty)?,
TypeDefKind::Stream(ty) => self.encode_stream(resolve, ty)?,
Expand Down
Loading