Skip to content

Commit cd32dd8

Browse files
authored
Teach guest Rust how to qualify Vec and String for no_std. (#488)
* Teach guest Rust how to qualify `Vec` and `String` for `no_std`. The Rust generator emits `use` declarations for `Vec` and `String` inside function bodies which usually works, but doesn't work when When `Vec` and `String` appear as return types. This teaches the Rust guest generator how to fully qualify `Vec` and `String` so that they always work, even in `no_std` mode when they aren't in scope by default. * Add a test, and fix more missing imports. * Add `no_std` to all the `generate!` invocations.
1 parent ff60653 commit cd32dd8

File tree

3 files changed

+210
-4
lines changed

3 files changed

+210
-4
lines changed

crates/gen-guest-rust/src/lib.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,14 @@ impl InterfaceGenerator<'_> {
492492

493493
self.push_str(" {\n");
494494

495+
uwrite!(
496+
self.src,
497+
"
498+
#[allow(unused_imports)]
499+
use wit_bindgen_guest_rust::rt::{{alloc, vec::Vec, string::String}};
500+
"
501+
);
502+
495503
// Finish out the macro-generated export implementation.
496504
macro_src.push_str(" {\n");
497505
let prefix = format!(
@@ -604,6 +612,14 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> {
604612
self.gen.opts.raw_strings
605613
}
606614

615+
fn vec_name(&self) -> &'static str {
616+
"wit_bindgen_guest_rust::rt::vec::Vec"
617+
}
618+
619+
fn string_name(&self) -> &'static str {
620+
"wit_bindgen_guest_rust::rt::string::String"
621+
}
622+
607623
fn default_param_mode(&self) -> TypeMode {
608624
self.default_param_mode
609625
}
Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
//! Like `codegen_tests` in codegen.rs, but with no_std.
2+
3+
#![no_std]
4+
#![allow(unused_macros)]
5+
6+
extern crate alloc;
7+
8+
mod codegen_tests {
9+
macro_rules! codegen_test {
10+
($id:ident $name:tt $test:tt) => {
11+
mod $id {
12+
wit_bindgen_guest_rust::generate!({
13+
path: $test,
14+
world: $name,
15+
no_std,
16+
});
17+
18+
#[test]
19+
fn works() {}
20+
21+
mod unchecked {
22+
wit_bindgen_guest_rust::generate!({
23+
path: $test,
24+
world: $name,
25+
unchecked,
26+
no_std,
27+
});
28+
29+
#[test]
30+
fn works() {}
31+
}
32+
}
33+
34+
};
35+
}
36+
test_helpers::codegen_tests!("*.wit");
37+
}
38+
39+
mod strings {
40+
use alloc::string::String;
41+
42+
wit_bindgen_guest_rust::generate!({
43+
inline: "
44+
default world not-used-name {
45+
import cat: interface {
46+
foo: func(x: string)
47+
bar: func() -> string
48+
}
49+
}
50+
",
51+
no_std,
52+
});
53+
54+
#[allow(dead_code)]
55+
fn test() {
56+
// Test the argument is `&str`.
57+
cat::foo("hello");
58+
59+
// Test the return type is `String`.
60+
let _t: String = cat::bar();
61+
}
62+
}
63+
64+
/// Like `strings` but with raw_strings`.
65+
mod raw_strings {
66+
use alloc::vec::Vec;
67+
68+
wit_bindgen_guest_rust::generate!({
69+
inline: "
70+
default world not-used-name {
71+
import cat: interface {
72+
foo: func(x: string)
73+
bar: func() -> string
74+
}
75+
}
76+
",
77+
raw_strings,
78+
no_std,
79+
});
80+
81+
#[allow(dead_code)]
82+
fn test() {
83+
// Test the argument is `&[u8]`.
84+
cat::foo(b"hello");
85+
86+
// Test the return type is `Vec<u8>`.
87+
let _t: Vec<u8> = cat::bar();
88+
}
89+
}
90+
91+
// This is a static compilation test to ensure that
92+
// export bindings can go inside of another mod/crate
93+
// and still compile.
94+
mod prefix {
95+
use alloc::{
96+
format,
97+
string::{String, ToString},
98+
};
99+
100+
mod bindings {
101+
wit_bindgen_guest_rust::generate!({
102+
inline: "
103+
default world baz {
104+
export exports1: interface {
105+
foo: func(x: string)
106+
bar: func() -> string
107+
}
108+
}
109+
",
110+
macro_call_prefix: "bindings::",
111+
no_std,
112+
});
113+
114+
pub(crate) use export_baz;
115+
}
116+
117+
struct Component;
118+
119+
impl bindings::exports1::Exports1 for Component {
120+
fn foo(x: String) {
121+
let _ = format!("foo: {}", x);
122+
}
123+
124+
fn bar() -> String {
125+
"bar".to_string()
126+
}
127+
}
128+
129+
bindings::export_baz!(Component);
130+
}
131+
132+
// This is a static compilation test to check that
133+
// the export macro name can be overridden.
134+
mod macro_name {
135+
use alloc::{format, string::String};
136+
137+
wit_bindgen_guest_rust::generate!({
138+
inline: "
139+
default world baz {
140+
export exports2: interface {
141+
foo: func(x: string)
142+
}
143+
}
144+
",
145+
export_macro_name: "jam",
146+
no_std,
147+
});
148+
149+
struct Component;
150+
151+
impl exports2::Exports2 for Component {
152+
fn foo(x: String) {
153+
let _ = format!("foo: {}", x);
154+
}
155+
}
156+
157+
jam!(Component);
158+
}
159+
160+
mod skip {
161+
wit_bindgen_guest_rust::generate!({
162+
inline: "
163+
default world baz {
164+
export exports: interface {
165+
foo: func()
166+
bar: func()
167+
}
168+
}
169+
",
170+
skip: ["foo"],
171+
no_std,
172+
});
173+
174+
struct Component;
175+
176+
impl exports::Exports for Component {
177+
fn bar() {}
178+
}
179+
180+
export_baz!(Component);
181+
}

crates/gen-rust-lib/src/lib.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@ pub trait RustGenerator<'a> {
2121
true
2222
}
2323

24+
/// Return the fully-qualified name for the `Vec` type to use.
25+
fn vec_name(&self) -> &'static str;
26+
27+
/// Return the fully-qualified name for the `String` type to use.
28+
fn string_name(&self) -> &'static str;
29+
2430
/// Return true iff the generator should use `&[u8]` instead of `&str` in bindings.
2531
fn use_raw_strings(&self) -> bool {
2632
false
@@ -181,9 +187,10 @@ pub trait RustGenerator<'a> {
181187
}
182188
TypeMode::Owned => {
183189
if self.use_raw_strings() {
184-
self.push_str("Vec<u8>")
190+
self.push_str(self.vec_name());
191+
self.push_str("::<u8>");
185192
} else {
186-
self.push_str("String")
193+
self.push_str(self.string_name());
187194
}
188195
}
189196
},
@@ -317,13 +324,15 @@ pub trait RustGenerator<'a> {
317324
if self.resolve().all_bits_valid(ty) {
318325
self.print_borrowed_slice(false, ty, lt);
319326
} else {
320-
self.push_str("Vec<");
327+
self.push_str(self.vec_name());
328+
self.push_str("::<");
321329
self.print_ty(ty, mode);
322330
self.push_str(">");
323331
}
324332
}
325333
TypeMode::Owned => {
326-
self.push_str("Vec<");
334+
self.push_str(self.vec_name());
335+
self.push_str("::<");
327336
self.print_ty(ty, mode);
328337
self.push_str(">");
329338
}

0 commit comments

Comments
 (0)