Skip to content

Commit 0e324eb

Browse files
authored
variant case (#1424)
* fix(cpp): support worlds with types Adds __wasm_export_ prefix to all C ABI export function names When a WIT world exports a function whose name matches the world/package name (e.g., world "foo" with export function "foo"), the generated C ABI function name foo() conflicted with the C++ namespace namespace foo. This generates functions like: ```c extern "C" __attribute__((__export_name__("foo"))) uint8_t * __wasm_export_foo() // C++ function name ``` The public C++ API is `exports::foo::Foo()`. Users never see the __wasm_export_foo() function in the bindings. Signed-off-by: Bailey Hayes <[email protected]> * feat(cpp): handle multiple return areas Signed-off-by: Bailey Hayes <[email protected]> * fix(cpp): mirror namespacing fix for imports Signed-off-by: Bailey Hayes <[email protected]> * fix(cpp): shared ret_area instance Signed-off-by: Bailey Hayes <[email protected]> --------- Signed-off-by: Bailey Hayes <[email protected]>
1 parent f11a006 commit 0e324eb

File tree

2 files changed

+101
-49
lines changed

2 files changed

+101
-49
lines changed

crates/cpp/src/lib.rs

Lines changed: 101 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -591,13 +591,14 @@ impl WorldGenerator for Cpp {
591591

592592
fn import_types(
593593
&mut self,
594-
_resolve: &Resolve,
594+
resolve: &Resolve,
595595
_world: WorldId,
596596
types: &[(&str, TypeId)],
597597
_files: &mut Files,
598598
) {
599-
for i in types.iter() {
600-
uwriteln!(self.h_src.src, "// import_type {}", i.0);
599+
let mut gen = self.interface(resolve, None, true, Some("$root".to_string()));
600+
for (name, id) in types.iter() {
601+
gen.define_type(name, *id);
601602
}
602603
}
603604

@@ -973,6 +974,8 @@ impl CppInterfaceGenerator<'_> {
973974
Some(ref module_name) => make_external_symbol(module_name, &func_name, symbol_variant),
974975
None => make_external_component(&func_name),
975976
};
977+
// Add prefix to C ABI export functions to avoid conflicts with C++ namespaces
978+
self.gen.c_src.src.push_str("__wasm_export_");
976979
if let Some(prefix) = self.gen.opts.export_prefix.as_ref() {
977980
self.gen.c_src.src.push_str(prefix);
978981
}
@@ -1335,7 +1338,8 @@ impl CppInterfaceGenerator<'_> {
13351338
f.needs_dealloc = needs_dealloc;
13361339
f.cabi_post = None;
13371340
abi::call(f.gen.resolve, variant, lift_lower, func, &mut f, false);
1338-
let code = String::from(f.src);
1341+
let ret_area_decl = f.emit_ret_area_if_needed();
1342+
let code = format!("{}{}", ret_area_decl, String::from(f.src));
13391343
self.gen.c_src.src.push_str(&code);
13401344
}
13411345
}
@@ -1379,8 +1383,9 @@ impl CppInterfaceGenerator<'_> {
13791383
let mut f = FunctionBindgen::new(self, params.clone());
13801384
f.params = params;
13811385
abi::post_return(f.gen.resolve, func, &mut f);
1382-
let FunctionBindgen { src, .. } = f;
1383-
self.gen.c_src.src.push_str(&src);
1386+
let ret_area_decl = f.emit_ret_area_if_needed();
1387+
let code = format!("{}{}", ret_area_decl, String::from(f.src));
1388+
self.gen.c_src.src.push_str(&code);
13841389
self.gen.c_src.src.push_str("}\n");
13851390
}
13861391
}
@@ -1636,7 +1641,8 @@ impl CppInterfaceGenerator<'_> {
16361641
result: &str,
16371642
variant: AbiVariant,
16381643
) -> (String, String) {
1639-
let extern_name = make_external_symbol(module_name, name, variant);
1644+
let mut extern_name = String::from("__wasm_import_");
1645+
extern_name.push_str(&make_external_symbol(module_name, name, variant));
16401646
let import = format!("extern \"C\" __attribute__((import_module(\"{module_name}\")))\n __attribute__((import_name(\"{name}\")))\n {result} {extern_name}({args});\n")
16411647
;
16421648
(extern_name, import)
@@ -2103,6 +2109,8 @@ struct FunctionBindgen<'a, 'b> {
21032109
cabi_post: Option<CabiPostInformation>,
21042110
needs_dealloc: bool,
21052111
leak_on_insertion: Option<String>,
2112+
return_pointer_area_size: ArchitectureSize,
2113+
return_pointer_area_align: Alignment,
21062114
}
21072115

21082116
impl<'a, 'b> FunctionBindgen<'a, 'b> {
@@ -2120,6 +2128,8 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
21202128
cabi_post: None,
21212129
needs_dealloc: false,
21222130
leak_on_insertion: None,
2131+
return_pointer_area_size: Default::default(),
2132+
return_pointer_area_align: Default::default(),
21232133
}
21242134
}
21252135

@@ -2191,6 +2201,47 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
21912201
operands[0]
21922202
);
21932203
}
2204+
2205+
/// Emits a shared return area declaration if needed by this function.
2206+
///
2207+
/// During code generation, `return_pointer()` may be called multiple times for:
2208+
/// - Indirect parameter storage (when too many/large params)
2209+
/// - Return value storage (when return type is too large)
2210+
///
2211+
/// **Safety:** This is safe because return pointers are used sequentially:
2212+
/// 1. Parameter marshaling (before call)
2213+
/// 2. Function execution
2214+
/// 3. Return value unmarshaling (after call)
2215+
///
2216+
/// The scratch space is reused but never accessed simultaneously.
2217+
fn emit_ret_area_if_needed(&self) -> String {
2218+
if !self.return_pointer_area_size.is_empty() {
2219+
let size_string = self
2220+
.return_pointer_area_size
2221+
.format(POINTER_SIZE_EXPRESSION);
2222+
let tp = match self.return_pointer_area_align {
2223+
Alignment::Bytes(bytes) => match bytes.get() {
2224+
1 => "uint8_t",
2225+
2 => "uint16_t",
2226+
4 => "uint32_t",
2227+
8 => "uint64_t",
2228+
// Fallback to uint8_t for unusual alignments (e.g., 16-byte SIMD).
2229+
// This is safe: the size calculation ensures correct buffer size,
2230+
// and uint8_t arrays can store any data regardless of alignment.
2231+
_ => "uint8_t",
2232+
},
2233+
Alignment::Pointer => "uintptr_t",
2234+
};
2235+
let static_var = if self.gen.in_guest_import {
2236+
""
2237+
} else {
2238+
"static "
2239+
};
2240+
format!("{static_var}{tp} ret_area[({size_string}+sizeof({tp})-1)/sizeof({tp})];\n")
2241+
} else {
2242+
String::new()
2243+
}
2244+
}
21942245
}
21952246

21962247
fn move_if_necessary(arg: &str) -> String {
@@ -2502,14 +2553,25 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
25022553
results.push(result);
25032554
}
25042555
abi::Instruction::HandleLower {
2505-
handle: Handle::Own(_ty),
2556+
handle: Handle::Own(ty),
25062557
..
25072558
} => {
25082559
let op = &operands[0];
2509-
if matches!(self.variant, AbiVariant::GuestImport) {
2510-
results.push(format!("{op}.into_handle()"));
2511-
} else {
2560+
2561+
// Check if this is an imported or exported resource
2562+
let resource_ty = &self.gen.resolve.types[*ty];
2563+
let resource_ty = match &resource_ty.kind {
2564+
TypeDefKind::Type(Type::Id(id)) => &self.gen.resolve.types[*id],
2565+
_ => resource_ty,
2566+
};
2567+
let is_exported = self.gen.is_exported_type(resource_ty);
2568+
2569+
if is_exported {
2570+
// Exported resources use .release()->handle
25122571
results.push(format!("{op}.release()->handle"));
2572+
} else {
2573+
// Imported resources use .into_handle()
2574+
results.push(format!("{op}.into_handle()"));
25132575
}
25142576
}
25152577
abi::Instruction::HandleLower {
@@ -2633,7 +2695,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
26332695
.join(", "),
26342696
);
26352697
self.src.push_str(">(");
2636-
self.src.push_str(&operands.join(", "));
2698+
self.src.push_str(
2699+
&operands
2700+
.iter()
2701+
.map(|op| move_if_necessary(op))
2702+
.collect::<Vec<_>>()
2703+
.join(", "),
2704+
);
26372705
self.src.push_str(");\n");
26382706
results.push(format!("std::move({name})"));
26392707
}
@@ -2715,14 +2783,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
27152783
variant.cases.iter().zip(blocks).zip(payloads).enumerate()
27162784
{
27172785
uwriteln!(self.src, "case {}: {{", i);
2718-
if let Some(ty) = case.ty.as_ref() {
2719-
let ty = self.gen.type_name(ty, &self.namespace, Flavor::InStruct);
2786+
if case.ty.is_some() {
27202787
let case =
27212788
format!("{elem_ns}::{}", to_c_ident(&case.name).to_pascal_case());
27222789
uwriteln!(
27232790
self.src,
2724-
"{} &{} = std::get<{case}>({}.variants).value;",
2725-
ty,
2791+
"auto& {} = std::get<{case}>({}.variants).value;",
27262792
payload,
27272793
operands[0],
27282794
);
@@ -2998,7 +3064,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
29983064
}
29993065
self.src.push_str(&func);
30003066
self.src.push_str("(");
3001-
self.src.push_str(&operands.join(", "));
3067+
self.src.push_str(
3068+
&operands
3069+
.iter()
3070+
.map(|op| move_if_necessary(op))
3071+
.collect::<Vec<_>>()
3072+
.join(", "),
3073+
);
30023074
self.src.push_str(");\n");
30033075
}
30043076
abi::Instruction::CallInterface { func, .. } => {
@@ -3015,7 +3087,13 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
30153087
}
30163088
self.src.push_str(&func_name_h);
30173089
self.push_str("(");
3018-
self.push_str(&operands.join(", "));
3090+
self.push_str(
3091+
&operands
3092+
.iter()
3093+
.map(|op| move_if_necessary(op))
3094+
.collect::<Vec<_>>()
3095+
.join(", "),
3096+
);
30193097
self.push_str(");\n");
30203098
if self.needs_dealloc {
30213099
uwriteln!(
@@ -3171,27 +3249,12 @@ impl<'a, 'b> Bindgen for FunctionBindgen<'a, 'b> {
31713249
}
31723250

31733251
fn return_pointer(&mut self, size: ArchitectureSize, align: Alignment) -> Self::Operand {
3252+
// Track maximum return area requirements
3253+
self.return_pointer_area_size = self.return_pointer_area_size.max(size);
3254+
self.return_pointer_area_align = self.return_pointer_area_align.max(align);
3255+
3256+
// Generate pointer to shared ret_area
31743257
let tmp = self.tmp();
3175-
let size_string = size.format(POINTER_SIZE_EXPRESSION);
3176-
let tp = match align {
3177-
Alignment::Bytes(bytes) => match bytes.get() {
3178-
1 => "uint8_t",
3179-
2 => "uint16_t",
3180-
4 => "uint32_t",
3181-
8 => "uint64_t",
3182-
_ => todo!(),
3183-
},
3184-
Alignment::Pointer => "uintptr_t",
3185-
};
3186-
let static_var = if self.gen.in_guest_import {
3187-
""
3188-
} else {
3189-
"static "
3190-
};
3191-
uwriteln!(
3192-
self.src,
3193-
"{static_var}{tp} ret_area[({size_string}+sizeof({tp})-1)/sizeof({tp})];"
3194-
);
31953258
uwriteln!(
31963259
self.src,
31973260
"{} ptr{tmp} = ({0})(&ret_area);",

crates/test/src/cpp.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,6 @@ impl LanguageMethods for Cpp {
4949
"async-trait-function.wit"
5050
| "error-context.wit"
5151
| "futures.wit"
52-
| "import_export_func.wit"
53-
| "import-func.wit"
54-
| "issue573.wit"
55-
| "issue929-no-export.wit"
56-
| "lift-lower-foreign.wit"
5752
| "lists.wit"
5853
| "multiversion"
5954
| "resource-alias.wit"
@@ -63,13 +58,7 @@ impl LanguageMethods for Cpp {
6358
| "resources-in-aggregates.wit"
6459
| "resources-with-futures.wit"
6560
| "resources-with-streams.wit"
66-
| "ret-areas.wit"
6761
| "return-resource-from-export.wit"
68-
| "same-names1.wit"
69-
| "same-names5.wit"
70-
| "variants.wit"
71-
| "variants-unioning-types.wit"
72-
| "worlds-with-types.wit"
7362
| "streams.wit" => true,
7463
_ => false,
7564
}

0 commit comments

Comments
 (0)