Skip to content

Add map support#1562

Draft
yordis wants to merge 1 commit intobytecodealliance:mainfrom
yordis:yordis/feat-map-support
Draft

Add map support#1562
yordis wants to merge 1 commit intobytecodealliance:mainfrom
yordis:yordis/feat-map-support

Conversation

@yordis
Copy link

@yordis yordis commented Mar 11, 2026

Summary

  • add end-to-end map<K, V> support in wit-bindgen core ABI and backend codegen paths (C, C++, C#, Go, MoonBit), plus Markdown type rendering
  • add map-focused codegen/runtime coverage in tests/codegen/map.wit and tests/runtime/map/*
  • fix Go test harness module replacement in crates/test/src/go.rs so generated map bindings resolve go.bytecodealliance.org/pkg/wit/* correctly

Test plan

  • cargo fmt
  • cargo clippy --workspace --all-targets -- -D warnings
  • cargo check
  • cargo check -p wit-bindgen-core
  • cargo check -p wit-bindgen-c
  • cargo check -p wit-bindgen-cpp
  • cargo check -p wit-bindgen-csharp
  • cargo check -p wit-bindgen-go
  • cargo check -p wit-bindgen-moonbit
  • cargo check -p wit-bindgen-markdown
  • cargo run test --artifacts target/artifacts --runner cargo --languages go --filter map.wit tests/codegen

Implement map type rendering plus lowering/lifting/deallocation support across the C, C++, C#, Go, MoonBit, and Markdown backends, and add map codegen/runtime tests.

This aligns non-Rust generators with core map ABI support and fixes the Go test harness module replacement path needed for map codegen verification.

Signed-off-by: Yordis Prieto <yordis.prieto@gmail.com>
@yordis yordis marked this pull request as draft March 11, 2026 04:51
}
}

Instruction::MapLower {
Copy link
Member

Choose a reason for hiding this comment

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

Could support for other languages be omitted unless tests are also added? I'm not equipped to handle other languages and forgive me if I'm wrong, but if the other languages' support is LLM generated I would perfer someone review the code who knows about the language generation.

&bindings_dir.join("go.mod"),
format!(
"module wit_component\n\ngo 1.25\n\nreplace go.bytecodealliance.org => {}",
"module wit_component\n\ngo 1.25\n\nreplace go.bytecodealliance.org/pkg => {}",
Copy link
Member

Choose a reason for hiding this comment

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

Is this perhaps a stray change? (otherwise I'm not sure where this comes from)

#[derive(PartialEq, Eq, Clone, Copy, Hash, Debug)]
enum RuntimeItem {
AllocCrate,
MapType,
Copy link
Member

Choose a reason for hiding this comment

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

This is a bit of a dated enum which shouldn't be neede any more, cousl Map refer to {rt}::Map directly in various locations instead of requiring a pub use?

Comment on lines +1943 to +1954
if mode.lists_borrowed {
let lifetime = mode.lifetime.unwrap();
self.push_str("&");
if lifetime != "'_" {
self.push_str(lifetime);
self.push_str(" ");
}
self.push_str("[(");
self.print_ty(key, key_mode);
self.push_str(", ");
self.print_ty(value, value_mode);
self.push_str(")]");
Copy link
Member

Choose a reason for hiding this comment

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

This looks like it might be copy/pasted from the lists side of things, but the slice syntax I don't think is intended here? Or is this intended? Should maps not be passed as &MapThing<K, V>?

Comment on lines +1789 to 1795
TypeDefKind::Map(key, value) => {
if mode.lifetime.is_some() {
self.print_map(key, value, mode);
return;
}
}
_ => {}
Copy link
Member

Choose a reason for hiding this comment

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

If adding this here, can you update the comment above?

Comment on lines +169 to 173
fn type_map(&mut self, id: TypeId, name: &str, key: &Type, value: &Type, docs: &Docs) {
let _ = (id, name, key, value, docs);
todo!("map types are not yet supported in this backend");
}
fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs);
Copy link
Member

Choose a reason for hiding this comment

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

Can this be a required method like all other methods in this trait? (e.g. no default impl)

TypeDefKind::Type(t) => self.read_from_memory(t, addr, offset),

TypeDefKind::List(_) => self.read_list_from_memory(ty, addr, offset),
TypeDefKind::Map(_, _) => self.read_list_from_memory(ty, addr, offset),
Copy link
Member

Choose a reason for hiding this comment

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

Can this have a comment for why read_list_from_memory works?

Type::Id(id) => match &self.resolve.types[id].kind {
TypeDefKind::Type(t) => self.write_to_memory(t, addr, offset),
TypeDefKind::List(_) => self.write_list_to_memory(ty, addr, offset),
TypeDefKind::Map(_, _) => self.write_list_to_memory(ty, addr, offset),
Copy link
Member

Choose a reason for hiding this comment

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

Similar to below, can this have a comment for why write_list_to_memory works?

Copy link
Member

Choose a reason for hiding this comment

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

Similar to other languages, I don't think this should be added without tests. Unlike other languages, though, I can review this, so mind adding some tests using the C bits?

Comment on lines +70 to +73
#[cfg(feature = "std")]
pub type Map<K, V> = std::collections::HashMap<K, V>;
#[cfg(not(feature = "std"))]
pub type Map<K, V> = alloc::collections::BTreeMap<K, V>;
Copy link
Member

Choose a reason for hiding this comment

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

This I'm hesitant to do myself, specifically switching based on std. That's not API-compatible unfortunately and puts this crate in a weird situation.

One possibility perhaps is to force some sort of configuration in bindgen! if map<K, V> is encountered which specifies which map type to use. Another is to always fall back to the more general BTreeMap in terms of availability. Another is to support both a fallback and a configuration. Another yet might be to avoid using HashMap and BTreeMap entirely and instead use Vec<(T, U)> by default. Overall I'm not sure what the best option is...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants