Skip to content

Commit cd60fc5

Browse files
committed
Add support for implements clause
The component model `implements` clause allows a world to import or export the same interface multiple times under different plain names. This threads the new `implements:Option<InterfaceId>` field from wit-parser through the `WorldGenerator` trait and all backends (Rust, C, C++, C#, Go, MoonBit, Markdown). A shared `wasm_import_module_name` helper produces the `[implements=<I>]label` binary encoding. Key per-backend concerns handled: - Type dedup: types are generated once per InterfaceId, not per label - Wasm names: all wasm-level import/export names use the implements encoding when applicable - Distinct bindings: each label gets its own module/namespace with independent function bindings
1 parent 7544b80 commit cd60fc5

15 files changed

Lines changed: 500 additions & 196 deletions

File tree

Cargo.lock

Lines changed: 88 additions & 103 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -46,13 +46,13 @@ prettyplease = "0.2.20"
4646
syn = { version = "2.0.89", features = ["printing"] }
4747
futures = "0.3.31"
4848

49-
wat = "1.245.1"
50-
wasmparser = "0.245.1"
51-
wasm-encoder = "0.245.1"
52-
wasm-metadata = { version = "0.245.1", default-features = false }
53-
wit-parser = "0.245.1"
54-
wit-component = "0.245.1"
55-
wasm-compose = "0.245.1"
49+
wat = { git = "https://github.com/ricochet/wasm-tools.git", branch = "wasmparser-implements" }
50+
wasmparser = { git = "https://github.com/ricochet/wasm-tools.git", branch = "wasmparser-implements" }
51+
wasm-encoder = { git = "https://github.com/ricochet/wasm-tools.git", branch = "wasmparser-implements" }
52+
wasm-metadata = { git = "https://github.com/ricochet/wasm-tools.git", branch = "wasmparser-implements", default-features = false }
53+
wit-parser = { git = "https://github.com/ricochet/wasm-tools.git", branch = "wasmparser-implements" }
54+
wit-component = { git = "https://github.com/ricochet/wasm-tools.git", branch = "wasmparser-implements" }
55+
wasm-compose = { git = "https://github.com/ricochet/wasm-tools.git", branch = "wasmparser-implements" }
5656

5757
wit-bindgen-core = { path = 'crates/core', version = '0.53.1' }
5858
wit-bindgen-c = { path = 'crates/c', version = '0.53.1' }

crates/c/src/lib.rs

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -211,18 +211,27 @@ impl WorldGenerator for C {
211211
resolve: &Resolve,
212212
name: &WorldKey,
213213
id: InterfaceId,
214+
implements: Option<InterfaceId>,
214215
_files: &mut Files,
215216
) -> Result<()> {
216-
let wasm_import_module = resolve.name_world_key(name);
217+
let wasm_import_module =
218+
wit_bindgen_core::wasm_import_module_name(resolve, name, implements);
217219
let mut r#gen = self.interface(resolve, true, Some(&wasm_import_module));
218220
r#gen.interface = Some((id, name));
221+
r#gen.implements = implements;
219222
r#gen.define_interface_types(id);
220223

221224
for (i, (_name, func)) in resolve.interfaces[id].functions.iter().enumerate() {
222225
if i == 0 {
223-
let name = resolve.name_world_key(name);
224-
uwriteln!(r#gen.src.h_fns, "\n// Imported Functions from `{name}`");
225-
uwriteln!(r#gen.src.c_fns, "\n// Imported Functions from `{name}`");
226+
let display_name = resolve.name_world_key(name);
227+
uwriteln!(
228+
r#gen.src.h_fns,
229+
"\n// Imported Functions from `{display_name}`"
230+
);
231+
uwriteln!(
232+
r#gen.src.c_fns,
233+
"\n// Imported Functions from `{display_name}`"
234+
);
226235
}
227236
r#gen.import(Some(name), func);
228237
}
@@ -259,17 +268,25 @@ impl WorldGenerator for C {
259268
resolve: &Resolve,
260269
name: &WorldKey,
261270
id: InterfaceId,
271+
implements: Option<InterfaceId>,
262272
_files: &mut Files,
263273
) -> Result<()> {
264274
let mut r#gen = self.interface(resolve, false, None);
265275
r#gen.interface = Some((id, name));
276+
r#gen.implements = implements;
266277
r#gen.define_interface_types(id);
267278

268279
for (i, (_name, func)) in resolve.interfaces[id].functions.iter().enumerate() {
269280
if i == 0 {
270-
let name = resolve.name_world_key(name);
271-
uwriteln!(r#gen.src.h_fns, "\n// Exported Functions from `{name}`");
272-
uwriteln!(r#gen.src.c_fns, "\n// Exported Functions from `{name}`");
281+
let display_name = resolve.name_world_key(name);
282+
uwriteln!(
283+
r#gen.src.h_fns,
284+
"\n// Exported Functions from `{display_name}`"
285+
);
286+
uwriteln!(
287+
r#gen.src.c_fns,
288+
"\n// Exported Functions from `{display_name}`"
289+
);
273290
}
274291
r#gen.export(func, Some(name));
275292
}
@@ -601,6 +618,7 @@ impl C {
601618
interface: None,
602619
in_import,
603620
wasm_import_module,
621+
implements: None,
604622
}
605623
}
606624

@@ -1297,6 +1315,7 @@ struct InterfaceGenerator<'a> {
12971315
resolve: &'a Resolve,
12981316
interface: Option<(InterfaceId, &'a WorldKey)>,
12991317
wasm_import_module: Option<&'a str>,
1318+
implements: Option<InterfaceId>,
13001319
}
13011320

13021321
impl C {
@@ -1893,6 +1912,10 @@ pub fn gen_type_name(resolve: &Resolve, ty: TypeId) -> (CTypeNameInfo<'_>, Strin
18931912
}
18941913

18951914
impl InterfaceGenerator<'_> {
1915+
fn wasm_name_world_key(&self, key: &WorldKey) -> String {
1916+
wit_bindgen_core::wasm_import_module_name(self.resolve, key, self.implements)
1917+
}
1918+
18961919
fn define_interface_types(&mut self, id: InterfaceId) {
18971920
let mut live = LiveTypes::default();
18981921
live.add_interface(self.resolve, id);
@@ -2122,7 +2145,7 @@ impl InterfaceGenerator<'_> {
21222145
self.src.c_fns,
21232146
"__attribute__((__import_module__(\"{}\"), __import_name__(\"{import_prefix}{}\")))",
21242147
match interface_name {
2125-
Some(name) => self.resolve.name_world_key(name),
2148+
Some(name) => self.wasm_name_world_key(name),
21262149
None => "$root".to_string(),
21272150
},
21282151
func.name
@@ -2282,7 +2305,7 @@ impl InterfaceGenerator<'_> {
22822305

22832306
self.src.c_fns("\n");
22842307

2285-
let core_module_name = interface_name.map(|s| self.resolve.name_world_key(s));
2308+
let core_module_name = interface_name.map(|s| self.wasm_name_world_key(s));
22862309
let export_name = func.legacy_core_export_name(core_module_name.as_deref());
22872310

22882311
// Print the actual header for this function into the header file, and
@@ -2368,7 +2391,7 @@ impl InterfaceGenerator<'_> {
23682391
);
23692392
uwriteln!(self.src.h_helpers, "void {name}_return({return_ty});");
23702393
let import_module = match interface_name {
2371-
Some(name) => self.resolve.name_world_key(name),
2394+
Some(name) => self.wasm_name_world_key(name),
23722395
None => "$root".to_string(),
23732396
};
23742397
uwriteln!(
@@ -2755,7 +2778,7 @@ void {name}_return({return_ty}) {{
27552778
let module = format!(
27562779
"{prefix}{}",
27572780
interface
2758-
.map(|name| self.resolve.name_world_key(name))
2781+
.map(|name| self.wasm_name_world_key(name))
27592782
.unwrap_or_else(|| "$root".into())
27602783
);
27612784
for (index, ty) in func

crates/core/src/lib.rs

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,28 @@ pub enum Direction {
2222
Export,
2323
}
2424

25+
/// Compute the wasm-level import/export module name for an interface,
26+
/// accounting for `implements` items.
27+
///
28+
/// For `implements` items, this produces the `[implements=<I>]label` encoding
29+
/// required by the component model binary format. Otherwise delegates to
30+
/// `resolve.name_world_key(name)`.
31+
pub fn wasm_import_module_name(
32+
resolve: &Resolve,
33+
name: &WorldKey,
34+
implements: Option<InterfaceId>,
35+
) -> String {
36+
match (name, implements) {
37+
(WorldKey::Name(label), Some(impl_id)) => {
38+
let iface_name = resolve
39+
.id_of(impl_id)
40+
.expect("unexpected anonymous interface");
41+
format!("[implements=<{iface_name}>]{label}")
42+
}
43+
_ => resolve.name_world_key(name),
44+
}
45+
}
46+
2547
pub trait WorldGenerator {
2648
fn generate(&mut self, resolve: &mut Resolve, id: WorldId, files: &mut Files) -> Result<()> {
2749
if self.uses_nominal_type_ids() {
@@ -42,8 +64,8 @@ pub trait WorldGenerator {
4264
for (name, import) in world.imports.iter() {
4365
match import {
4466
WorldItem::Function(f) => funcs.push((unwrap_name(name), f)),
45-
WorldItem::Interface { id, .. } => {
46-
self.import_interface(resolve, name, *id, files)?
67+
WorldItem::Interface { id, implements, .. } => {
68+
self.import_interface(resolve, name, *id, *implements, files)?
4769
}
4870
WorldItem::Type { id, .. } => types.push((unwrap_name(name), *id)),
4971
}
@@ -68,7 +90,9 @@ pub trait WorldGenerator {
6890
for (name, export) in world.exports.iter() {
6991
match export {
7092
WorldItem::Function(f) => funcs.push((unwrap_name(name), f)),
71-
WorldItem::Interface { id, .. } => interfaces.push((name, id)),
93+
WorldItem::Interface { id, implements, .. } => {
94+
interfaces.push((name, id, implements))
95+
}
7296
WorldItem::Type { .. } => unreachable!(),
7397
}
7498
}
@@ -78,8 +102,8 @@ pub trait WorldGenerator {
78102

79103
self.pre_export_interface(resolve, files)?;
80104

81-
for (name, id) in interfaces {
82-
self.export_interface(resolve, name, *id, files)?;
105+
for (name, id, implements) in interfaces {
106+
self.export_interface(resolve, name, *id, *implements, files)?;
83107
}
84108
self.finish(resolve, id, files)
85109
}
@@ -104,6 +128,7 @@ pub trait WorldGenerator {
104128
resolve: &Resolve,
105129
name: &WorldKey,
106130
iface: InterfaceId,
131+
implements: Option<InterfaceId>,
107132
files: &mut Files,
108133
) -> Result<()>;
109134

@@ -118,6 +143,7 @@ pub trait WorldGenerator {
118143
resolve: &Resolve,
119144
name: &WorldKey,
120145
iface: InterfaceId,
146+
implements: Option<InterfaceId>,
121147
files: &mut Files,
122148
) -> Result<()>;
123149
fn import_funcs(

crates/cpp/src/lib.rs

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ struct Cpp {
108108
// needed for symmetric disambiguation
109109
interface_prefixes: HashMap<(Direction, WorldKey), String>,
110110
import_prefix: Option<String>,
111+
/// Tracks InterfaceIds whose types have already been generated (for implements dedup).
112+
implements_types_generated: HashSet<InterfaceId>,
111113
}
112114

113115
#[cfg(feature = "clap")]
@@ -330,6 +332,7 @@ impl Cpp {
330332
sizes,
331333
in_guest_import,
332334
wasm_import_module,
335+
implements_label: None,
333336
}
334337
}
335338

@@ -513,6 +516,7 @@ impl WorldGenerator for Cpp {
513516
resolve: &Resolve,
514517
name: &WorldKey,
515518
id: InterfaceId,
519+
implements: Option<InterfaceId>,
516520
_files: &mut Files,
517521
) -> anyhow::Result<()> {
518522
self.imported_interfaces.insert(id);
@@ -528,13 +532,19 @@ impl WorldGenerator for Cpp {
528532
}
529533

530534
let store = self.start_new_file(None);
531-
let wasm_import_module = resolve.name_world_key(name);
535+
let wasm_import_module =
536+
wit_bindgen_core::wasm_import_module_name(resolve, name, implements);
532537
let binding = Some(name);
538+
let should_gen_types = self.implements_types_generated.insert(id);
533539
let mut r#gen = self.interface(resolve, binding, true, Some(wasm_import_module));
534540
r#gen.interface = Some(id);
535-
r#gen.types(id);
536-
let namespace =
537-
namespace(resolve, &TypeOwner::Interface(id), false, &r#gen.r#gen.opts);
541+
if let (WorldKey::Name(label), Some(_)) = (name, implements) {
542+
r#gen.implements_label = Some((label.clone(), false));
543+
}
544+
if should_gen_types {
545+
r#gen.types(id);
546+
}
547+
let namespace = r#gen.freestanding_namespace(id, false);
538548

539549
for (_name, func) in resolve.interfaces[id].functions.iter() {
540550
if matches!(func.kind, FunctionKind::Freestanding) {
@@ -564,6 +574,7 @@ impl WorldGenerator for Cpp {
564574
resolve: &Resolve,
565575
name: &WorldKey,
566576
id: InterfaceId,
577+
implements: Option<InterfaceId>,
567578
_files: &mut Files,
568579
) -> anyhow::Result<()> {
569580
let old_prefix = self.opts.export_prefix.clone();
@@ -579,12 +590,19 @@ impl WorldGenerator for Cpp {
579590
.src
580591
.push_str(&format!("// export_interface {name:?}\n"));
581592
self.imported_interfaces.remove(&id);
582-
let wasm_import_module = resolve.name_world_key(name);
593+
let wasm_import_module =
594+
wit_bindgen_core::wasm_import_module_name(resolve, name, implements);
583595
let binding = Some(name);
596+
let should_gen_types = self.implements_types_generated.insert(id);
584597
let mut r#gen = self.interface(resolve, binding, false, Some(wasm_import_module));
585598
r#gen.interface = Some(id);
586-
r#gen.types(id);
587-
let namespace = namespace(resolve, &TypeOwner::Interface(id), true, &r#gen.r#gen.opts);
599+
if let (WorldKey::Name(label), Some(_)) = (name, implements) {
600+
r#gen.implements_label = Some((label.clone(), true));
601+
}
602+
if should_gen_types {
603+
r#gen.types(id);
604+
}
605+
let namespace = r#gen.freestanding_namespace(id, true);
588606

589607
for (_name, func) in resolve.interfaces[id].functions.iter() {
590608
if matches!(func.kind, FunctionKind::Freestanding) {
@@ -762,7 +780,6 @@ impl WorldGenerator for Cpp {
762780
}
763781
}
764782

765-
// determine namespace (for the lifted C++ function)
766783
fn namespace(resolve: &Resolve, owner: &TypeOwner, guest_export: bool, opts: &Opts) -> Vec<String> {
767784
let mut result = Vec::default();
768785
if let Some(prefix) = &opts.internal_prefix {
@@ -850,9 +867,35 @@ struct CppInterfaceGenerator<'a> {
850867
sizes: SizeAlign,
851868
in_guest_import: bool,
852869
pub wasm_import_module: Option<String>,
870+
/// When generating for an implements item, the label and whether it's an export.
871+
implements_label: Option<(String, bool)>,
853872
}
854873

855874
impl CppInterfaceGenerator<'_> {
875+
/// Compute the namespace for freestanding functions in an interface.
876+
/// Uses `implements_label` when set, otherwise falls back to the standard
877+
/// namespace computation.
878+
fn freestanding_namespace(&self, iface: InterfaceId, guest_export: bool) -> Vec<String> {
879+
if let Some((label, is_export)) = &self.implements_label {
880+
let mut ns = Vec::new();
881+
if let Some(prefix) = &self.r#gen.opts.internal_prefix {
882+
ns.push(prefix.clone());
883+
}
884+
if *is_export {
885+
ns.push(String::from("exports"));
886+
}
887+
ns.push(to_c_ident(label));
888+
ns
889+
} else {
890+
namespace(
891+
self.resolve,
892+
&TypeOwner::Interface(iface),
893+
guest_export,
894+
&self.r#gen.opts,
895+
)
896+
}
897+
}
898+
856899
fn types(&mut self, iface: InterfaceId) {
857900
let iface_data = &self.resolve().interfaces[iface];
858901

@@ -923,7 +966,20 @@ impl CppInterfaceGenerator<'_> {
923966
.map(TypeOwner::Interface)
924967
.unwrap_or(TypeOwner::World(self.r#gen.world_id.unwrap())),
925968
));
926-
let mut namespace = namespace(self.resolve, &owner, guest_export, &self.r#gen.opts);
969+
let mut namespace = if let Some((label, is_export)) = &self.implements_label {
970+
// For implements items, use the label as namespace
971+
let mut ns = Vec::new();
972+
if let Some(prefix) = &self.r#gen.opts.internal_prefix {
973+
ns.push(prefix.clone());
974+
}
975+
if *is_export {
976+
ns.push(String::from("exports"));
977+
}
978+
ns.push(to_c_ident(label));
979+
ns
980+
} else {
981+
namespace(self.resolve, &owner, guest_export, &self.r#gen.opts)
982+
};
927983
let is_drop = is_special_method(func);
928984
let func_name_h = if !matches!(&func.kind, FunctionKind::Freestanding) {
929985
namespace.push(object.clone());

0 commit comments

Comments
 (0)