diff --git a/crates/wac-graph/src/encoding.rs b/crates/wac-graph/src/encoding.rs index 4a1c172..a290387 100644 --- a/crates/wac-graph/src/encoding.rs +++ b/crates/wac-graph/src/encoding.rs @@ -311,6 +311,7 @@ impl<'a> TypeEncoder<'a> { DefinedType::Tuple(types) => self.tuple(state, types), DefinedType::List(ty) => self.list(state, *ty), DefinedType::FixedSizeList(ty, elements) => self.fixed_size_list(state, *ty, *elements), + DefinedType::Map(key, value) => self.map(state, *key, *value), DefinedType::Option(ty) => self.option(state, *ty), DefinedType::Result { ok, err } => self.result(state, *ok, *err), DefinedType::Variant(v) => self.variant(state, v), @@ -640,6 +641,14 @@ impl<'a> TypeEncoder<'a> { index } + fn map(&self, state: &mut State, key: ValueType, value: ValueType) -> u32 { + let key = self.value_type(state, key); + let value = self.value_type(state, value); + let index = state.current.encodable.type_count(); + state.current.encodable.ty().defined_type().map(key, value); + index + } + fn fixed_size_list(&self, state: &mut State, ty: ValueType, elements: u32) -> u32 { let ty = self.value_type(state, ty); let index = state.current.encodable.type_count(); diff --git a/crates/wac-graph/tests/graphs/map-type-mismatch/error.txt b/crates/wac-graph/tests/graphs/map-type-mismatch/error.txt new file mode 100644 index 0000000..ece8493 --- /dev/null +++ b/crates/wac-graph/tests/graphs/map-type-mismatch/error.txt @@ -0,0 +1,8 @@ +failed to add argument edge from source node 2 to target node 1 referenced in argument 0 for test case `map-type-mismatch` + +Caused by: + 0: mismatched instantiation argument `test:foo/consume` + 1: mismatched type for export `lookup` + 2: mismatched type for function parameter `m` + 3: mismatched type for map value + 4: expected u64, found u32 diff --git a/crates/wac-graph/tests/graphs/map-type-mismatch/foo.wit b/crates/wac-graph/tests/graphs/map-type-mismatch/foo.wit new file mode 100644 index 0000000..cc8ffb9 --- /dev/null +++ b/crates/wac-graph/tests/graphs/map-type-mismatch/foo.wit @@ -0,0 +1,14 @@ +package test:foo; + +interface produce { + lookup: func(m: map) -> u32; +} + +interface consume { + lookup: func(m: map) -> u32; +} + +world w { + import consume; + export produce; +} diff --git a/crates/wac-graph/tests/graphs/map-type-mismatch/graph.json b/crates/wac-graph/tests/graphs/map-type-mismatch/graph.json new file mode 100644 index 0000000..d711012 --- /dev/null +++ b/crates/wac-graph/tests/graphs/map-type-mismatch/graph.json @@ -0,0 +1,30 @@ +{ + "packages": [ + { + "name": "test:foo", + "path": "foo.wit" + } + ], + "nodes": [ + { + "type": "instantiation", + "package": 0 + }, + { + "type": "instantiation", + "package": 0 + }, + { + "type": "alias", + "source": 0, + "export": "test:foo/produce" + } + ], + "arguments": [ + { + "source": 2, + "target": 1, + "name": "test:foo/consume" + } + ] +} \ No newline at end of file diff --git a/crates/wac-graph/tests/graphs/map-types/encoded.wat b/crates/wac-graph/tests/graphs/map-types/encoded.wat new file mode 100644 index 0000000..28754d2 --- /dev/null +++ b/crates/wac-graph/tests/graphs/map-types/encoded.wat @@ -0,0 +1,63 @@ +(component + (type (;0;) + (instance + (type (;0;) (map string string)) + (export (;1;) "headers" (type (eq 0))) + (type (;2;) (map string u32)) + (type (;3;) (option u32)) + (type (;4;) (func (param "m" 2) (result 3))) + (export (;0;) "lookup" (func (type 4))) + (type (;5;) (list u8)) + (type (;6;) (map string 5)) + (type (;7;) (func (param "h" 1) (result 6))) + (export (;1;) "bundle" (func (type 7))) + ) + ) + (import "test:foo/i" (instance (;0;) (type 0))) + (type (;1;) + (component + (type (;0;) + (instance + (type (;0;) (map string string)) + (export (;1;) "headers" (type (eq 0))) + (type (;2;) (map string u32)) + (type (;3;) (option u32)) + (type (;4;) (func (param "m" 2) (result 3))) + (export (;0;) "lookup" (func (type 4))) + (type (;5;) (list u8)) + (type (;6;) (map string 5)) + (type (;7;) (func (param "h" 1) (result 6))) + (export (;1;) "bundle" (func (type 7))) + ) + ) + (import "test:foo/i" (instance (;0;) (type 0))) + (type (;1;) + (instance + (type (;0;) (map string string)) + (export (;1;) "headers" (type (eq 0))) + (type (;2;) (map string u32)) + (type (;3;) (option u32)) + (type (;4;) (func (param "m" 2) (result 3))) + (export (;0;) "lookup" (func (type 4))) + (type (;5;) (list u8)) + (type (;6;) (map string 5)) + (type (;7;) (func (param "h" 1) (result 6))) + (export (;1;) "bundle" (func (type 7))) + ) + ) + (export (;1;) "test:foo/i" (instance (type 1))) + ) + ) + (import "unlocked-dep=" (component (;0;) (type 1))) + (instance (;1;) (instantiate 0 + (with "test:foo/i" (instance 0)) + ) + ) + (alias export 1 "test:foo/i" (instance (;2;))) + (instance (;3;) (instantiate 0 + (with "test:foo/i" (instance 2)) + ) + ) + (alias export 3 "test:foo/i" (instance (;4;))) + (export (;5;) "test:foo/i" (instance 4)) +) diff --git a/crates/wac-graph/tests/graphs/map-types/foo.wit b/crates/wac-graph/tests/graphs/map-types/foo.wit new file mode 100644 index 0000000..d1d5136 --- /dev/null +++ b/crates/wac-graph/tests/graphs/map-types/foo.wit @@ -0,0 +1,12 @@ +package test:foo; + +interface i { + type headers = map; + lookup: func(m: map) -> option; + bundle: func(h: headers) -> map>; +} + +world w { + import i; + export i; +} diff --git a/crates/wac-graph/tests/graphs/map-types/graph.json b/crates/wac-graph/tests/graphs/map-types/graph.json new file mode 100644 index 0000000..1870b2b --- /dev/null +++ b/crates/wac-graph/tests/graphs/map-types/graph.json @@ -0,0 +1,41 @@ +{ + "packages": [ + { + "name": "test:foo", + "path": "foo.wit" + } + ], + "nodes": [ + { + "type": "instantiation", + "package": 0 + }, + { + "type": "instantiation", + "package": 0 + }, + { + "type": "alias", + "source": 0, + "export": "test:foo/i" + }, + { + "type": "alias", + "source": 1, + "export": "test:foo/i" + } + ], + "arguments": [ + { + "source": 2, + "target": 1, + "name": "test:foo/i" + } + ], + "exports": [ + { + "node": 3, + "name": "test:foo/i" + } + ] +} diff --git a/crates/wac-types/src/aggregator.rs b/crates/wac-types/src/aggregator.rs index 1b14421..2cb6131 100644 --- a/crates/wac-types/src/aggregator.rs +++ b/crates/wac-types/src/aggregator.rs @@ -871,6 +871,10 @@ impl TypeAggregator { DefinedType::FixedSizeList(ty, elements) => { DefinedType::FixedSizeList(self.remap_value_type(types, *ty, checker)?, *elements) } + DefinedType::Map(key, value) => DefinedType::Map( + self.remap_value_type(types, *key, checker)?, + self.remap_value_type(types, *value, checker)?, + ), DefinedType::Option(ty) => { DefinedType::Option(self.remap_value_type(types, *ty, checker)?) } diff --git a/crates/wac-types/src/checker.rs b/crates/wac-types/src/checker.rs index 9ce5432..0592808 100644 --- a/crates/wac-types/src/checker.rs +++ b/crates/wac-types/src/checker.rs @@ -583,6 +583,12 @@ impl<'a> SubtypeChecker<'a> { self.value_type(*a, at, *b, bt) .context("mismatched type for fixed size list element") } + (DefinedType::Map(akey, avalue), DefinedType::Map(bkey, bvalue)) => { + self.value_type(*akey, at, *bkey, bt) + .context("mismatched type for map key")?; + self.value_type(*avalue, at, *bvalue, bt) + .context("mismatched type for map value") + } (DefinedType::Future(a), DefinedType::Future(b)) => self .payload(*a, at, *b, bt) .context("mismatched type for future payload"), @@ -616,6 +622,7 @@ impl<'a> SubtypeChecker<'a> { (DefinedType::Tuple(_), _) | (DefinedType::List(_), _) | (DefinedType::FixedSizeList(_, _), _) + | (DefinedType::Map(_, _), _) | (DefinedType::Option(_), _) | (DefinedType::Result { .. }, _) | (DefinedType::Variant(_), _) diff --git a/crates/wac-types/src/component.rs b/crates/wac-types/src/component.rs index 126f29a..47a1d05 100644 --- a/crates/wac-types/src/component.rs +++ b/crates/wac-types/src/component.rs @@ -661,6 +661,8 @@ pub enum DefinedType { List(ValueType), /// A fixed size array FixedSizeList(ValueType, u32), + /// A map type. + Map(ValueType, ValueType), /// An option type. Option(ValueType), /// A result type. @@ -692,6 +694,7 @@ impl DefinedType { match self { Self::Tuple(tys) => tys.iter().any(|ty| ty.contains_borrow(types)), Self::List(ty) | Self::FixedSizeList(ty, _) => ty.contains_borrow(types), + Self::Map(key, value) => key.contains_borrow(types) || value.contains_borrow(types), Self::Option(ty) => ty.contains_borrow(types), Self::Result { ok, err } => { ok.map(|ty| ty.contains_borrow(types)).unwrap_or(false) @@ -726,6 +729,10 @@ impl DefinedType { DefinedType::List(ty) | DefinedType::Option(ty) | DefinedType::FixedSizeList(ty, _) => { ty._visit_defined_types(types, visitor, false) } + DefinedType::Map(key, value) => { + key._visit_defined_types(types, visitor, false)?; + value._visit_defined_types(types, visitor, false) + } DefinedType::Result { ok, err } => { if let Some(ty) = ok.as_ref() { ty._visit_defined_types(types, visitor, false)?; @@ -768,6 +775,7 @@ impl DefinedType { Self::Tuple(_) => "tuple", Self::List(_) => "list", Self::FixedSizeList(_, _) => "list<,N>", + Self::Map(_, _) => "map", Self::Option(_) => "option", Self::Result { .. } => "result", Self::Variant(_) => "variant", diff --git a/crates/wac-types/src/package.rs b/crates/wac-types/src/package.rs index f5590ea..0ebdb94 100644 --- a/crates/wac-types/src/package.rs +++ b/crates/wac-types/src/package.rs @@ -773,8 +773,13 @@ impl<'a> TypeConverter<'a> { .add_defined_type(DefinedType::FixedSizeList(ty, *length)), ) } - wasm::ComponentDefinedType::Map { .. } => { - bail!("ComponentDefinedType::Map is not yet supported"); + wasm::ComponentDefinedType::Map { key, value, .. } => { + let key_ty = self.component_val_type(*key)?; + let value_ty = self.component_val_type(*value)?; + ValueType::Defined( + self.types + .add_defined_type(DefinedType::Map(key_ty, value_ty)), + ) } };