Skip to content

Commit a4e4f7a

Browse files
authored
no_std support in the Rust guest bindings. (#345)
* no_std support in the Rust guest bindings. This adds no_std support in the Rust guest bindings. Mostly this involves using `core` and `alloc` instead of `std`, but it also involves adding `extern crate alloc;` in a few places, and also adding a `"std"` cargo feature to gen-guest-rust so that the `impl std::error::Error` can be made conditional. This will eventually be useful for generating bindings from the WASI wit files for std itself to use. And, it's useful for experimenting with generating minimal bindings. * Factor out the `extern crate alloc` into the guest-rust crate. * Re-enable the "macros" feature in the demo build. * Switch from a "std" cargo feature to a `no_std` config option. * Move re-exports into the `rt` module.
1 parent fc35377 commit a4e4f7a

File tree

4 files changed

+49
-16
lines changed

4 files changed

+49
-16
lines changed

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

+20-8
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ pub struct Opts {
5353
/// crate.
5454
#[cfg_attr(feature = "clap", arg(skip))]
5555
pub standalone: bool,
56+
57+
/// If true, code generation should avoid any features that depend on `std`.
58+
#[cfg_attr(feature = "clap", arg(long))]
59+
pub no_std: bool,
5660
}
5761

5862
#[derive(Default)]
@@ -92,6 +96,10 @@ impl RustWasm {
9296
}
9397

9498
impl RustGenerator for RustWasm {
99+
fn use_std(&self) -> bool {
100+
!self.opts.no_std
101+
}
102+
95103
fn default_param_mode(&self) -> TypeMode {
96104
if self.in_import {
97105
// We default to borrowing as much as possible to maximize the ability
@@ -167,6 +175,10 @@ impl Generator for RustWasm {
167175
));
168176
}
169177

178+
self.src.push_str("#[allow(unused_imports)]");
179+
self.src
180+
.push_str("use wit_bindgen_guest_rust::rt::{alloc, vec::Vec, string::String};");
181+
170182
self.sizes.fill(iface);
171183
}
172184

@@ -758,14 +770,14 @@ impl FunctionBindgen<'_> {
758770
fn emit_cleanup(&mut self) {
759771
for (ptr, layout) in mem::take(&mut self.cleanup) {
760772
self.push_str(&format!(
761-
"if {layout}.size() != 0 {{\nstd::alloc::dealloc({ptr}, {layout});\n}}\n"
773+
"if {layout}.size() != 0 {{\nalloc::dealloc({ptr}, {layout});\n}}\n"
762774
));
763775
}
764776
if self.needs_cleanup_list {
765777
self.push_str(
766778
"for (ptr, layout) in cleanup_list {\n
767779
if layout.size() != 0 {\n
768-
std::alloc::dealloc(ptr, layout);\n
780+
alloc::dealloc(ptr, layout);\n
769781
}\n
770782
}\n",
771783
);
@@ -1231,7 +1243,7 @@ impl Bindgen for FunctionBindgen<'_> {
12311243
assert_eq!(none, "()");
12321244
let operand = &operands[0];
12331245
let invalid = if unchecked {
1234-
"std::hint::unreachable_unchecked()"
1246+
"core::hint::unreachable_unchecked()"
12351247
} else {
12361248
"panic!(\"invalid enum discriminant\")"
12371249
};
@@ -1268,7 +1280,7 @@ impl Bindgen for FunctionBindgen<'_> {
12681280
let ok = self.blocks.pop().unwrap();
12691281
let operand = &operands[0];
12701282
let invalid = if unchecked {
1271-
"std::hint::unreachable_unchecked()"
1283+
"core::hint::unreachable_unchecked()"
12721284
} else {
12731285
"panic!(\"invalid enum discriminant\")"
12741286
};
@@ -1400,15 +1412,15 @@ impl Bindgen for FunctionBindgen<'_> {
14001412
let size = self.gen.sizes.size(element);
14011413
let align = self.gen.sizes.align(element);
14021414
self.push_str(&format!(
1403-
"let {layout} = core::alloc::Layout::from_size_align_unchecked({vec}.len() * {size}, {align});\n",
1415+
"let {layout} = alloc::Layout::from_size_align_unchecked({vec}.len() * {size}, {align});\n",
14041416
));
14051417
self.push_str(&format!(
1406-
"let {result} = if {layout}.size() != 0\n{{\nlet ptr = std::alloc::alloc({layout});\n",
1418+
"let {result} = if {layout}.size() != 0\n{{\nlet ptr = alloc::alloc({layout});\n",
14071419
));
14081420
self.push_str(&format!(
1409-
"if ptr.is_null()\n{{\nstd::alloc::handle_alloc_error({layout});\n}}\nptr\n}}",
1421+
"if ptr.is_null()\n{{\nalloc::handle_alloc_error({layout});\n}}\nptr\n}}",
14101422
));
1411-
self.push_str(&format!("else {{\nstd::ptr::null_mut()\n}};\n",));
1423+
self.push_str(&format!("else {{\ncore::ptr::null_mut()\n}};\n",));
14121424
self.push_str(&format!("for (i, e) in {vec}.into_iter().enumerate() {{\n",));
14131425
self.push_str(&format!(
14141426
"let base = {result} as i32 + (i as i32) * {size};\n",

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

+10-3
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ pub enum TypeMode {
1414
}
1515

1616
pub trait RustGenerator {
17+
/// Return true iff the generator can use `std` features in its output.
18+
fn use_std(&self) -> bool {
19+
true
20+
}
21+
1722
fn push_str(&mut self, s: &str);
1823
fn info(&self, ty: TypeId) -> TypeInfo;
1924
fn types_mut(&mut self) -> &mut Types;
@@ -828,9 +833,11 @@ pub trait RustGenerator {
828833
self.push_str("}\n");
829834
self.push_str("}\n");
830835
self.push_str("\n");
831-
self.push_str("impl std::error::Error for ");
832-
self.push_str(&name);
833-
self.push_str("{}\n");
836+
if self.use_std() {
837+
self.push_str("impl std::error::Error for ");
838+
self.push_str(&name);
839+
self.push_str("{}\n");
840+
}
834841
} else {
835842
self.print_rust_enum_debug(
836843
id,

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

+6
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ mod kw {
5252
syn::custom_keyword!(paths);
5353
syn::custom_keyword!(unchecked);
5454
syn::custom_keyword!(multi_module);
55+
syn::custom_keyword!(no_std);
5556
}
5657

5758
impl Parse for Opts {
@@ -69,6 +70,7 @@ impl Parse for Opts {
6970
ConfigField::Unchecked => opts.unchecked = true,
7071
ConfigField::MultiModule => opts.multi_module = true,
7172
ConfigField::Interfaces(v) => interfaces = v,
73+
ConfigField::NoStd => opts.no_std = true,
7274
}
7375
}
7476
if interfaces.is_empty() {
@@ -104,6 +106,7 @@ enum ConfigField {
104106
Interfaces(Vec<Interface>),
105107
Unchecked,
106108
MultiModule,
109+
NoStd,
107110
}
108111

109112
impl Parse for ConfigField {
@@ -141,6 +144,9 @@ impl Parse for ConfigField {
141144
} else if l.peek(kw::multi_module) {
142145
input.parse::<kw::multi_module>()?;
143146
Ok(ConfigField::MultiModule)
147+
} else if l.peek(kw::no_std) {
148+
input.parse::<kw::no_std>()?;
149+
Ok(ConfigField::NoStd)
144150
} else {
145151
Err(l.error())
146152
}

crates/guest-rust/src/lib.rs

+13-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
use std::fmt;
2-
use std::marker;
3-
use std::mem;
4-
use std::ops::Deref;
1+
#![no_std]
2+
3+
extern crate alloc;
4+
5+
use alloc::boxed::Box;
6+
use core::fmt;
7+
use core::marker;
8+
use core::mem;
9+
use core::ops::Deref;
510

611
#[cfg(feature = "macros")]
712
pub use wit_bindgen_guest_rust_macro::{export, import};
@@ -123,7 +128,10 @@ pub unsafe trait LocalHandle: HandleType {
123128

124129
#[doc(hidden)]
125130
pub mod rt {
126-
use std::alloc::{self, Layout};
131+
use super::alloc::alloc::Layout;
132+
133+
// Re-export things from liballoc for convenient use.
134+
pub use super::alloc::{alloc, string, vec};
127135

128136
#[no_mangle]
129137
unsafe extern "C" fn cabi_realloc(

0 commit comments

Comments
 (0)