Skip to content

Commit ba3a6e5

Browse files
Implement optional --enable-method-chaining for the Rust generator.
1 parent 7b5c1c6 commit ba3a6e5

6 files changed

Lines changed: 130 additions & 21 deletions

File tree

crates/rust/src/bindgen.rs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ pub(super) struct FunctionBindgen<'a, 'b> {
2121
pub import_return_pointer_area_align: Alignment,
2222
pub handle_decls: Vec<String>,
2323
always_owned: bool,
24+
return_self: bool,
2425
}
2526

2627
pub const POINTER_SIZE_EXPRESSION: &str = "::core::mem::size_of::<*const u8>()";
@@ -31,6 +32,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
3132
params: Vec<String>,
3233
wasm_import_module: &'b str,
3334
always_owned: bool,
35+
return_self: bool,
3436
) -> FunctionBindgen<'a, 'b> {
3537
FunctionBindgen {
3638
r#gen,
@@ -45,6 +47,7 @@ impl<'a, 'b> FunctionBindgen<'a, 'b> {
4547
import_return_pointer_area_align: Default::default(),
4648
handle_decls: Vec::new(),
4749
always_owned,
50+
return_self,
4851
}
4952
}
5053

@@ -1049,7 +1052,11 @@ impl Bindgen for FunctionBindgen<'_, '_> {
10491052
}
10501053

10511054
Instruction::Return { amt, .. } => match amt {
1052-
0 => {}
1055+
0 => {
1056+
if self.return_self {
1057+
uwriteln!(self.src, "self");
1058+
}
1059+
}
10531060
1 => {
10541061
self.push_str(&operands[0]);
10551062
self.push_str("\n");

crates/rust/src/interface.rs

Lines changed: 45 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -768,7 +768,7 @@ pub mod vtable{ordinal} {{
768768
}
769769

770770
fn lower_to_memory(&mut self, address: &str, value: &str, ty: &Type, module: &str) -> String {
771-
let mut f = FunctionBindgen::new(self, Vec::new(), module, true);
771+
let mut f = FunctionBindgen::new(self, Vec::new(), module, true, false);
772772
abi::lower_to_memory(f.r#gen.resolve, &mut f, address.into(), value.into(), ty);
773773
format!("unsafe {{ {} }}", String::from(f.src))
774774
}
@@ -780,7 +780,7 @@ pub mod vtable{ordinal} {{
780780
indirect: bool,
781781
module: &str,
782782
) -> String {
783-
let mut f = FunctionBindgen::new(self, Vec::new(), module, true);
783+
let mut f = FunctionBindgen::new(self, Vec::new(), module, true, false);
784784
abi::deallocate_lists_in_types(f.r#gen.resolve, types, operands, indirect, &mut f);
785785
format!("unsafe {{ {} }}", String::from(f.src))
786786
}
@@ -792,13 +792,13 @@ pub mod vtable{ordinal} {{
792792
indirect: bool,
793793
module: &str,
794794
) -> String {
795-
let mut f = FunctionBindgen::new(self, Vec::new(), module, true);
795+
let mut f = FunctionBindgen::new(self, Vec::new(), module, true, false);
796796
abi::deallocate_lists_and_own_in_types(f.r#gen.resolve, types, operands, indirect, &mut f);
797797
format!("unsafe {{ {} }}", String::from(f.src))
798798
}
799799

800800
fn lift_from_memory(&mut self, address: &str, ty: &Type, module: &str) -> String {
801-
let mut f = FunctionBindgen::new(self, Vec::new(), module, true);
801+
let mut f = FunctionBindgen::new(self, Vec::new(), module, true, false);
802802
let result = abi::lift_from_memory(f.r#gen.resolve, &mut f, address.into(), ty);
803803
format!("unsafe {{ {}\n{result} }}", String::from(f.src))
804804
}
@@ -809,7 +809,13 @@ pub mod vtable{ordinal} {{
809809
func: &Function,
810810
params: Vec<String>,
811811
) {
812-
let mut f = FunctionBindgen::new(self, params, module, false);
812+
let mut f = FunctionBindgen::new(
813+
self,
814+
params,
815+
module,
816+
false,
817+
matches!(&func.kind, FunctionKind::Method(_)) && self.r#gen.opts.enable_method_chaining,
818+
);
813819
abi::call(
814820
f.r#gen.resolve,
815821
AbiVariant::GuestImport,
@@ -1032,7 +1038,7 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
10321038
}
10331039
lowers.push("ParamsLower(_ptr,)".to_string());
10341040
} else {
1035-
let mut f = FunctionBindgen::new(self, Vec::new(), module, true);
1041+
let mut f = FunctionBindgen::new(self, Vec::new(), module, true, false);
10361042
let mut results = Vec::new();
10371043
for (i, Param { ty, .. }) in func.params.iter().enumerate() {
10381044
let name = format!("_lower{i}");
@@ -1078,8 +1084,17 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
10781084
}
10791085
uwriteln!(
10801086
self.src,
1081-
"_MySubtask {{ _unused: core::marker::PhantomData }}.call(({})).await",
1082-
params.join(" ")
1087+
"_MySubtask {{ _unused: core::marker::PhantomData }}.call(({})).await{}",
1088+
params.join(" "),
1089+
if let (FunctionKind::Method(_), None, true) = (
1090+
&func.kind,
1091+
func.result,
1092+
self.r#gen.opts.enable_method_chaining
1093+
) {
1094+
";\nself"
1095+
} else {
1096+
""
1097+
}
10831098
);
10841099
}
10851100

@@ -1121,7 +1136,7 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
11211136
);
11221137
}
11231138

1124-
let mut f = FunctionBindgen::new(self, params, self.wasm_import_module, false);
1139+
let mut f = FunctionBindgen::new(self, params, self.wasm_import_module, false, false);
11251140
let variant = if async_ {
11261141
AbiVariant::GuestExportAsync
11271142
} else {
@@ -1191,7 +1206,7 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
11911206
let params = self.print_post_return_sig(func);
11921207
self.src.push_str("{ unsafe {\n");
11931208

1194-
let mut f = FunctionBindgen::new(self, params, self.wasm_import_module, false);
1209+
let mut f = FunctionBindgen::new(self, params, self.wasm_import_module, false, false);
11951210
abi::post_return(f.r#gen.resolve, func, &mut f);
11961211
let FunctionBindgen {
11971212
needs_cleanup_list,
@@ -1439,19 +1454,29 @@ unsafe fn call_import(&mut self, _params: Self::ParamsLower, _results: *mut u8)
14391454
fn print_signature(&mut self, func: &Function, params_owned: bool, sig: &FnSig) -> Vec<String> {
14401455
let params = self.print_docs_and_params(func, params_owned, sig);
14411456
self.push_str(" -> ");
1442-
if let FunctionKind::Constructor(resource_id) = &func.kind {
1443-
match classify_constructor_return_type(&self.resolve, *resource_id, &func.result) {
1444-
ConstructorReturnType::Self_ => {
1445-
self.push_str("Self");
1457+
match &func.kind {
1458+
FunctionKind::Constructor(resource_id) => {
1459+
match classify_constructor_return_type(&self.resolve, *resource_id, &func.result) {
1460+
ConstructorReturnType::Self_ => {
1461+
self.push_str("Self");
1462+
}
1463+
ConstructorReturnType::Result { err } => {
1464+
self.push_str("Result<Self, ");
1465+
self.print_result_type(&err);
1466+
self.push_str("> where Self: Sized");
1467+
}
14461468
}
1447-
ConstructorReturnType::Result { err } => {
1448-
self.push_str("Result<Self, ");
1449-
self.print_result_type(&err);
1450-
self.push_str("> where Self: Sized");
1469+
}
1470+
FunctionKind::Method(_) => {
1471+
if self.r#gen.opts.enable_method_chaining && func.result.is_none() {
1472+
self.push_str("&Self");
1473+
} else {
1474+
self.print_result_type(&func.result);
14511475
}
14521476
}
1453-
} else {
1454-
self.print_result_type(&func.result);
1477+
_ => {
1478+
self.print_result_type(&func.result);
1479+
}
14551480
}
14561481
params
14571482
}

crates/rust/src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,10 @@ pub struct Opts {
300300
arg(long, require_equals = true, value_name = "true|false")
301301
)]
302302
pub merge_structurally_equal_types: Option<Option<bool>>,
303+
304+
/// If true, methods normally returning `()` instead return `&Self`. This applies to both imported and exported methods.
305+
#[cfg_attr(feature = "clap", arg(long))]
306+
pub enable_method_chaining: bool,
303307
}
304308

305309
impl Opts {
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
//@ args = '--enable-method-chaining'
2+
3+
include!(env!("BINDINGS"));
4+
5+
use crate::foo::bar::i::A;
6+
7+
struct Component;
8+
export!(Component);
9+
10+
impl Guest for Component {
11+
fn run() {
12+
let my_a = A::new();
13+
my_a.set_a(42).set_b(true).do_();
14+
}
15+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//@ args = '--enable-method-chaining'
2+
3+
include!(env!("BINDINGS"));
4+
5+
use crate::exports::foo::bar::i::{Guest, GuestA};
6+
use std::cell::Cell;
7+
8+
struct Component;
9+
export!(Component);
10+
impl Guest for Component {
11+
type A = MyA;
12+
}
13+
14+
struct MyA {
15+
prop_a: Cell<u32>,
16+
prop_b: Cell<bool>,
17+
}
18+
19+
impl GuestA for MyA {
20+
fn new() -> MyA {
21+
MyA {
22+
prop_a: Cell::new(0),
23+
prop_b: Cell::new(false),
24+
}
25+
}
26+
27+
fn set_a(&self, a: u32) -> &Self {
28+
self.prop_a.set(a);
29+
self
30+
}
31+
32+
fn set_b(&self, b: bool) -> &Self {
33+
self.prop_b.set(b);
34+
self
35+
}
36+
37+
fn do_(&self) -> &Self {
38+
self
39+
}
40+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package foo:bar;
2+
3+
interface i {
4+
resource a {
5+
constructor();
6+
set-a: func(arg: u32);
7+
set-b: func(arg: bool);
8+
do: func();
9+
}
10+
}
11+
world runner {
12+
import i;
13+
14+
export run: func();
15+
}
16+
world test {
17+
export i;
18+
}

0 commit comments

Comments
 (0)