Skip to content

Commit fafe05f

Browse files
authored
Make the Rust bindings friendlier for ahead-of-time generation. (#506)
In place of `no_std`, add a `std_feature` option, which tells the code generator to add `#[cfg(feature = "std")]` to any use of `std`. This allows the same bindings support either std or no-std depending on the cargo flags in the crate using them. And, in place of `unchecked`, use `#[cfg(not(debug_assertion))]`, so that we automatically disable checks in release builds. This way we can have a bindings crate with one copy of the generated output that works across std and no_std, and disables checks in release builds.
1 parent 0fb4018 commit fafe05f

File tree

6 files changed

+166
-161
lines changed

6 files changed

+166
-161
lines changed

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

Lines changed: 127 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,10 @@ pub struct Opts {
3131
#[cfg_attr(feature = "clap", arg(long))]
3232
pub rustfmt: bool,
3333

34-
/// Whether or not the bindings assume interface values are always
35-
/// well-formed or whether checks are performed.
34+
/// If true, code generation should qualify any features that depend on
35+
/// `std` with `cfg(feature = "std")`.
3636
#[cfg_attr(feature = "clap", arg(long))]
37-
pub unchecked: bool,
38-
39-
/// If true, code generation should avoid any features that depend on `std`.
40-
#[cfg_attr(feature = "clap", arg(long))]
41-
pub no_std: bool,
37+
pub std_feature: bool,
4238

4339
/// If true, adds `#[macro_export]` to the `export_*!` macro generated to
4440
/// export it from the Rust crate.
@@ -622,8 +618,8 @@ impl<'a> RustGenerator<'a> for InterfaceGenerator<'a> {
622618
}
623619
}
624620

625-
fn use_std(&self) -> bool {
626-
!self.gen.opts.no_std
621+
fn std_feature(&self) -> bool {
622+
self.gen.opts.std_feature
627623
}
628624

629625
fn use_raw_strings(&self) -> bool {
@@ -937,7 +933,6 @@ impl Bindgen for FunctionBindgen<'_, '_> {
937933
operands: &mut Vec<String>,
938934
results: &mut Vec<String>,
939935
) {
940-
let unchecked = self.gen.gen.opts.unchecked;
941936
let mut top_as = |cvt: &str| {
942937
let mut s = operands.pop().unwrap();
943938
s.push_str(" as ");
@@ -995,17 +990,15 @@ impl Bindgen for FunctionBindgen<'_, '_> {
995990
Instruction::U32FromI32 => top_as("u32"),
996991
Instruction::U64FromI64 => top_as("u64"),
997992
Instruction::CharFromI32 => {
998-
if unchecked {
999-
results.push(format!(
1000-
"core::char::from_u32_unchecked({} as u32)",
1001-
operands[0]
1002-
));
1003-
} else {
1004-
results.push(format!(
1005-
"core::char::from_u32({} as u32).unwrap()",
1006-
operands[0]
1007-
));
1008-
}
993+
results.push(format!(
994+
"{{
995+
#[cfg(not(debug_assertions))]
996+
{{ core::char::from_u32_unchecked({} as u32) }}
997+
#[cfg(debug_assertions)]
998+
{{ core::char::from_u32({} as u32).unwrap() }}
999+
}}",
1000+
operands[0], operands[0]
1001+
));
10091002
}
10101003

10111004
Instruction::Bitcasts { casts } => {
@@ -1016,21 +1009,21 @@ impl Bindgen for FunctionBindgen<'_, '_> {
10161009
results.push(format!("match {} {{ true => 1, false => 0 }}", operands[0]));
10171010
}
10181011
Instruction::BoolFromI32 => {
1019-
if unchecked {
1020-
results.push(format!(
1021-
"core::mem::transmute::<u8, bool>({} as u8)",
1022-
operands[0],
1023-
));
1024-
} else {
1025-
results.push(format!(
1026-
"match {} {{
1027-
0 => false,
1028-
1 => true,
1029-
_ => panic!(\"invalid bool discriminant\"),
1030-
}}",
1031-
operands[0],
1032-
));
1033-
}
1012+
results.push(format!(
1013+
"{{
1014+
#[cfg(not(debug_assertions))]
1015+
{{ core::mem::transmute::<u8, bool>({} as u8) }}
1016+
#[cfg(debug_assertions)]
1017+
{{
1018+
match {} {{
1019+
0 => false,
1020+
1 => true,
1021+
_ => panic!(\"invalid bool discriminant\"),
1022+
}}
1023+
}}
1024+
}}",
1025+
operands[0], operands[0],
1026+
));
10341027
}
10351028

10361029
Instruction::FlagsLower { flags, .. } => {
@@ -1095,47 +1088,62 @@ impl Bindgen for FunctionBindgen<'_, '_> {
10951088
self.push_str("};\n");
10961089
}
10971090

1098-
// In unchecked mode when this type is a named enum then we know we
1099-
// defined the type so we can transmute directly into it.
1100-
Instruction::VariantLift { name, variant, .. }
1101-
if variant.cases.iter().all(|c| c.ty.is_none()) && unchecked =>
1102-
{
1103-
self.blocks.drain(self.blocks.len() - variant.cases.len()..);
1104-
let mut result = format!("core::mem::transmute::<_, ");
1105-
result.push_str(&name.to_upper_camel_case());
1106-
result.push_str(">(");
1107-
result.push_str(&operands[0]);
1108-
result.push_str(" as ");
1109-
result.push_str(int_repr(variant.tag()));
1110-
result.push_str(")");
1111-
results.push(result);
1112-
}
1091+
Instruction::VariantLift {
1092+
name, variant, ty, ..
1093+
} => {
1094+
let mut result = String::new();
1095+
result.push_str("{");
11131096

1114-
Instruction::VariantLift { variant, ty, .. } => {
1097+
let named_enum = variant.cases.iter().all(|c| c.ty.is_none());
11151098
let blocks = self
11161099
.blocks
11171100
.drain(self.blocks.len() - variant.cases.len()..)
11181101
.collect::<Vec<_>>();
11191102
let op0 = &operands[0];
1120-
let mut result = format!("match {op0} {{\n");
1103+
1104+
if named_enum {
1105+
// In unchecked mode when this type is a named enum then we know we
1106+
// defined the type so we can transmute directly into it.
1107+
result.push_str("#[cfg(not(debug_assertions))]");
1108+
result.push_str("{");
1109+
result.push_str("core::mem::transmute::<_, ");
1110+
result.push_str(&name.to_upper_camel_case());
1111+
result.push_str(">(");
1112+
result.push_str(op0);
1113+
result.push_str(" as ");
1114+
result.push_str(int_repr(variant.tag()));
1115+
result.push_str(")");
1116+
result.push_str("}");
1117+
}
1118+
1119+
if named_enum {
1120+
result.push_str("#[cfg(debug_assertions)]");
1121+
}
1122+
result.push_str("{");
1123+
result.push_str(&format!("match {op0} {{\n"));
11211124
let name = self.typename_lift(*ty);
11221125
for (i, (case, block)) in variant.cases.iter().zip(blocks).enumerate() {
1123-
let pat = if i == variant.cases.len() - 1 && unchecked {
1124-
String::from("_")
1125-
} else {
1126-
i.to_string()
1127-
};
1126+
let pat = i.to_string();
11281127
let block = if case.ty.is_some() {
11291128
format!("({block})")
11301129
} else {
11311130
String::new()
11321131
};
11331132
let case = case.name.to_upper_camel_case();
1134-
result.push_str(&format!("{pat} => {name}::{case}{block},\n"));
1135-
}
1136-
if !unchecked {
1137-
result.push_str("_ => panic!(\"invalid enum discriminant\"),\n");
1133+
if i == variant.cases.len() - 1 {
1134+
result.push_str("#[cfg(debug_assertions)]");
1135+
result.push_str(&format!("{pat} => {name}::{case}{block},\n"));
1136+
result.push_str("#[cfg(not(debug_assertions))]");
1137+
result.push_str(&format!("_ => {name}::{case}{block},\n"));
1138+
} else {
1139+
result.push_str(&format!("{pat} => {name}::{case}{block},\n"));
1140+
}
11381141
}
1142+
result.push_str("#[cfg(debug_assertions)]");
1143+
result.push_str("_ => panic!(\"invalid enum discriminant\"),\n");
1144+
result.push_str("}");
1145+
result.push_str("}");
1146+
11391147
result.push_str("}");
11401148
results.push(result);
11411149
}
@@ -1174,17 +1182,19 @@ impl Bindgen for FunctionBindgen<'_, '_> {
11741182
.zip(blocks)
11751183
.enumerate()
11761184
{
1177-
let pat = if i == union.cases.len() - 1 && unchecked {
1178-
String::from("_")
1179-
} else {
1180-
i.to_string()
1181-
};
1185+
let pat = i.to_string();
11821186
let name = self.typename_lift(*ty);
1183-
result.push_str(&format!("{pat} => {name}::{case_name}({block}),\n"));
1184-
}
1185-
if !unchecked {
1186-
result.push_str("_ => panic!(\"invalid union discriminant\"),\n");
1187+
if i == union.cases.len() - 1 {
1188+
result.push_str("#[cfg(debug_assertions)]");
1189+
result.push_str(&format!("{pat} => {name}::{case_name}({block}),\n"));
1190+
result.push_str("#[cfg(not(debug_assertions))]");
1191+
result.push_str(&format!("_ => {name}::{case_name}({block}),\n"));
1192+
} else {
1193+
result.push_str(&format!("{pat} => {name}::{case_name}({block}),\n"));
1194+
}
11871195
}
1196+
result.push_str("#[cfg(debug_assertions)]");
1197+
result.push_str("_ => panic!(\"invalid union discriminant\"),\n");
11881198
result.push_str("}");
11891199
results.push(result);
11901200
}
@@ -1210,16 +1220,14 @@ impl Bindgen for FunctionBindgen<'_, '_> {
12101220
let none = self.blocks.pop().unwrap();
12111221
assert_eq!(none, "()");
12121222
let operand = &operands[0];
1213-
let invalid = if unchecked {
1214-
"core::hint::unreachable_unchecked()"
1215-
} else {
1216-
"panic!(\"invalid enum discriminant\")"
1217-
};
12181223
results.push(format!(
12191224
"match {operand} {{
12201225
0 => None,
12211226
1 => Some({some}),
1222-
_ => {invalid},
1227+
#[cfg(not(debug_assertions))]
1228+
_ => core::hint::unreachable_unchecked(),
1229+
#[cfg(debug_assertions)]
1230+
_ => panic!(\"invalid enum discriminant\"),
12231231
}}"
12241232
));
12251233
}
@@ -1247,16 +1255,14 @@ impl Bindgen for FunctionBindgen<'_, '_> {
12471255
let err = self.blocks.pop().unwrap();
12481256
let ok = self.blocks.pop().unwrap();
12491257
let operand = &operands[0];
1250-
let invalid = if unchecked {
1251-
"core::hint::unreachable_unchecked()"
1252-
} else {
1253-
"panic!(\"invalid enum discriminant\")"
1254-
};
12551258
results.push(format!(
12561259
"match {operand} {{
12571260
0 => Ok({ok}),
12581261
1 => Err({err}),
1259-
_ => {invalid},
1262+
#[cfg(not(debug_assertions))]
1263+
_ => core::hint::unreachable_unchecked(),
1264+
#[cfg(debug_assertions)]
1265+
_ => panic!(\"invalid enum discriminant\"),
12601266
}}"
12611267
));
12621268
}
@@ -1272,21 +1278,14 @@ impl Bindgen for FunctionBindgen<'_, '_> {
12721278
results.push(result);
12731279
}
12741280

1275-
// In unchecked mode when this type is a named enum then we know we
1276-
// defined the type so we can transmute directly into it.
1277-
Instruction::EnumLift { enum_, ty, .. } if unchecked => {
1278-
let mut result = format!("core::mem::transmute::<_, ");
1279-
result.push_str(&self.gen.type_path(*ty, true));
1280-
result.push_str(">(");
1281-
result.push_str(&operands[0]);
1282-
result.push_str(" as ");
1283-
result.push_str(int_repr(enum_.tag()));
1284-
result.push_str(")");
1285-
results.push(result);
1286-
}
1287-
12881281
Instruction::EnumLift { enum_, ty, .. } => {
1289-
let mut result = format!("match ");
1282+
let mut result = String::new();
1283+
result.push_str("{");
1284+
1285+
// In checked mode do a `match`.
1286+
result.push_str("#[cfg(debug_assertions)]");
1287+
result.push_str("{");
1288+
result.push_str("match ");
12901289
result.push_str(&operands[0]);
12911290
result.push_str(" {\n");
12921291
let name = self.gen.type_path(*ty, true);
@@ -1295,6 +1294,22 @@ impl Bindgen for FunctionBindgen<'_, '_> {
12951294
result.push_str(&format!("{i} => {name}::{case},\n"));
12961295
}
12971296
result.push_str("_ => panic!(\"invalid enum discriminant\"),\n");
1297+
result.push_str("}");
1298+
result.push_str("}");
1299+
1300+
// In unchecked mode when this type is a named enum then we know we
1301+
// defined the type so we can transmute directly into it.
1302+
result.push_str("#[cfg(not(debug_assertions))]");
1303+
result.push_str("{");
1304+
result.push_str("core::mem::transmute::<_, ");
1305+
result.push_str(&self.gen.type_path(*ty, true));
1306+
result.push_str(">(");
1307+
result.push_str(&operands[0]);
1308+
result.push_str(" as ");
1309+
result.push_str(int_repr(enum_.tag()));
1310+
result.push_str(")");
1311+
result.push_str("}");
1312+
12981313
result.push_str("}");
12991314
results.push(result);
13001315
}
@@ -1360,10 +1375,22 @@ impl Bindgen for FunctionBindgen<'_, '_> {
13601375
);
13611376
if self.gen.gen.opts.raw_strings {
13621377
results.push(result);
1363-
} else if unchecked {
1364-
results.push(format!("String::from_utf8_unchecked({})", result));
13651378
} else {
1366-
results.push(format!("String::from_utf8({}).unwrap()", result));
1379+
let mut converted = String::new();
1380+
converted.push_str("{");
1381+
1382+
converted.push_str("#[cfg(not(debug_assertions))]");
1383+
converted.push_str("{");
1384+
converted.push_str(&format!("String::from_utf8_unchecked({})", result));
1385+
converted.push_str("}");
1386+
1387+
converted.push_str("#[cfg(debug_assertions)]");
1388+
converted.push_str("{");
1389+
converted.push_str(&format!("String::from_utf8({}).unwrap()", result));
1390+
converted.push_str("}");
1391+
1392+
converted.push_str("}");
1393+
results.push(converted);
13671394
}
13681395
}
13691396

crates/gen-guest-rust/tests/codegen.rs

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,17 +8,6 @@ mod codegen_tests {
88

99
#[test]
1010
fn works() {}
11-
12-
mod unchecked {
13-
wit_bindgen::generate!({
14-
path: $test,
15-
world: $name,
16-
unchecked,
17-
});
18-
19-
#[test]
20-
fn works() {}
21-
}
2211
}
2312

2413
};

0 commit comments

Comments
 (0)