Merge pull request #3128 from rtfeldman/multi-arch-bindgen

Multi-arch bindgen
This commit is contained in:
Richard Feldman 2022-05-24 13:26:29 -04:00 committed by GitHub
commit 8bcc815661
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 604 additions and 973 deletions

5
Cargo.lock generated
View File

@ -3459,6 +3459,7 @@ dependencies = [
"cli_utils", "cli_utils",
"ctor", "ctor",
"dircpy", "dircpy",
"indexmap",
"indoc", "indoc",
"pretty_assertions", "pretty_assertions",
"roc_builtins", "roc_builtins",
@ -3473,6 +3474,8 @@ dependencies = [
"roc_target", "roc_target",
"roc_test_utils", "roc_test_utils",
"roc_types", "roc_types",
"strum",
"strum_macros",
"target-lexicon", "target-lexicon",
"tempfile", "tempfile",
] ]
@ -4113,6 +4116,8 @@ dependencies = [
name = "roc_target" name = "roc_target"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"strum",
"strum_macros",
"target-lexicon", "target-lexicon",
] ]

View File

@ -28,6 +28,9 @@ roc_error_macros = { path = "../error_macros" }
bumpalo = { version = "3.8.0", features = ["collections"] } bumpalo = { version = "3.8.0", features = ["collections"] }
target-lexicon = "0.12.3" target-lexicon = "0.12.3"
clap = { version = "3.1.15", default-features = false, features = ["std", "color", "suggestions", "derive"] } clap = { version = "3.1.15", default-features = false, features = ["std", "color", "suggestions", "derive"] }
strum = "0.24.0"
strum_macros = "0.24"
indexmap = "1.8.1"
[dev-dependencies] [dev-dependencies]
pretty_assertions = "1.0.0" pretty_assertions = "1.0.0"

File diff suppressed because it is too large Load Diff

View File

@ -8,15 +8,17 @@ use roc_can::{
use roc_load::{LoadedModule, Threading}; use roc_load::{LoadedModule, Threading};
use roc_mono::layout::LayoutCache; use roc_mono::layout::LayoutCache;
use roc_reporting::report::RenderTarget; use roc_reporting::report::RenderTarget;
use roc_target::Architecture;
use std::io; use std::io;
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use strum::IntoEnumIterator;
use target_lexicon::Triple; use target_lexicon::Triple;
pub fn load_types( pub fn load_types(
full_file_path: PathBuf, full_file_path: PathBuf,
dir: &Path, dir: &Path,
threading: Threading, threading: Threading,
) -> Result<Types, io::Error> { ) -> Result<Vec<(Architecture, Types)>, io::Error> {
// TODO: generate both 32-bit and 64-bit #[cfg] macros if structs are different // TODO: generate both 32-bit and 64-bit #[cfg] macros if structs are different
// depending on 32-bit vs 64-bit targets. // depending on 32-bit vs 64-bit targets.
let target_info = (&Triple::host()).into(); let target_info = (&Triple::host()).into();
@ -56,7 +58,10 @@ pub fn load_types(
); );
} }
let mut layout_cache = LayoutCache::new(target_info); let mut answer = Vec::with_capacity(Architecture::iter().size_hint().0);
for architecture in Architecture::iter() {
let mut layout_cache = LayoutCache::new(architecture.into());
let mut env = Env { let mut env = Env {
arena, arena,
layout_cache: &mut layout_cache, layout_cache: &mut layout_cache,
@ -65,27 +70,24 @@ pub fn load_types(
enum_names: Default::default(), enum_names: Default::default(),
subs, subs,
}; };
let mut types = Types::default(); let mut types = Types::default();
for decl in decls.into_iter() { for decl in decls.iter() {
let defs = match decl { let defs = match decl {
Declaration::Declare(def) => { Declaration::Declare(def) => {
vec![def] vec![def.clone()]
} }
Declaration::DeclareRec(defs, cycle_mark) => { Declaration::DeclareRec(defs, cycle_mark) => {
if cycle_mark.is_illegal(subs) { if cycle_mark.is_illegal(subs) {
vec![] Vec::new()
} else { } else {
defs defs.clone()
} }
} }
Declaration::Builtin(..) => { Declaration::Builtin(..) => {
unreachable!("Builtin decl in userspace module?") unreachable!("Builtin decl in userspace module?")
} }
Declaration::InvalidCycle(..) => { Declaration::InvalidCycle(..) => Vec::new(),
vec![]
}
}; };
for Def { for Def {
@ -107,5 +109,8 @@ pub fn load_types(
} }
} }
Ok(types) answer.push((architecture, types));
}
Ok(answer)
} }

View File

@ -57,30 +57,21 @@ pub fn main() {
}; };
match load_types(input_path.clone(), &cwd, Threading::AllAvailable) { match load_types(input_path.clone(), &cwd, Threading::AllAvailable) {
Ok(types) => { Ok(types_by_architecture) => {
let mut buf; let mut buf;
let result = match output_type { match output_type {
OutputType::Rust => { OutputType::Rust => {
buf = std::str::from_utf8(bindgen_rs::HEADER).unwrap().to_string(); buf = std::str::from_utf8(bindgen_rs::HEADER).unwrap().to_string();
let body = bindgen_rs::emit(&types_by_architecture);
bindgen_rs::write_types(&types, &mut buf) buf.push_str(&body);
} }
OutputType::C => todo!("TODO: Generate bindings for C"), OutputType::C => todo!("TODO: Generate bindings for C"),
OutputType::Zig => todo!("TODO: Generate bindings for Zig"), OutputType::Zig => todo!("TODO: Generate bindings for Zig"),
OutputType::Json => todo!("TODO: Generate bindings for JSON"), OutputType::Json => todo!("TODO: Generate bindings for JSON"),
}; };
if let Err(err) = result {
eprintln!(
"Unable to generate binding string {} - {:?}",
output_path.display(),
err
);
process::exit(1);
}
let mut file = File::create(output_path.clone()).unwrap_or_else(|err| { let mut file = File::create(output_path.clone()).unwrap_or_else(|err| {
eprintln!( eprintln!(
"Unable to create output file {} - {:?}", "Unable to create output file {} - {:?}",

View File

@ -9,7 +9,7 @@ use std::convert::TryInto;
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct TypeId(usize); pub struct TypeId(usize);
#[derive(Default, Debug)] #[derive(Default, Debug, Clone)]
pub struct Types { pub struct Types {
by_id: Vec<RocType>, by_id: Vec<RocType>,

View File

@ -1,8 +1,10 @@
mod bindings; mod bindings;
use bindings::NonRecursive;
extern "C" { extern "C" {
#[link_name = "roc__mainForHost_1_exposed_generic"] #[link_name = "roc__mainForHost_1_exposed_generic"]
fn roc_main(_: *mut bindings::NonRecursive); fn roc_main(_: *mut NonRecursive);
} }
#[no_mangle] #[no_mangle]
@ -11,8 +13,7 @@ pub extern "C" fn rust_main() -> i32 {
use std::collections::hash_set::HashSet; use std::collections::hash_set::HashSet;
let tag_union = unsafe { let tag_union = unsafe {
let mut ret: core::mem::MaybeUninit<bindings::NonRecursive> = let mut ret: core::mem::MaybeUninit<NonRecursive> = core::mem::MaybeUninit::uninit();
core::mem::MaybeUninit::uninit();
roc_main(ret.as_mut_ptr()); roc_main(ret.as_mut_ptr());
@ -30,11 +31,11 @@ pub extern "C" fn rust_main() -> i32 {
println!( println!(
"tag_union was: {:?}\n`Foo \"small str\"` is: {:?}\n`Foo \"A long enough string to not be small\"` is: {:?}\n`Bar 123` is: {:?}\n`Baz` is: {:?}\n`Blah 456` is: {:?}", "tag_union was: {:?}\n`Foo \"small str\"` is: {:?}\n`Foo \"A long enough string to not be small\"` is: {:?}\n`Bar 123` is: {:?}\n`Baz` is: {:?}\n`Blah 456` is: {:?}",
tag_union, tag_union,
bindings::NonRecursive::Foo("small str".into()), NonRecursive::Foo("small str".into()),
bindings::NonRecursive::Foo("A long enough string to not be small".into()), NonRecursive::Foo("A long enough string to not be small".into()),
bindings::NonRecursive::Bar(123.into()), NonRecursive::Bar(123.into()),
bindings::NonRecursive::Baz, NonRecursive::Baz,
bindings::NonRecursive::Blah(456), NonRecursive::Blah(456),
); // Debug ); // Debug
let mut set = HashSet::new(); let mut set = HashSet::new();

View File

@ -27,6 +27,13 @@ mod test_gen_rs {
.unwrap_or_default(), .unwrap_or_default(),
indoc!( indoc!(
r#" r#"
#[cfg(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "wasm32"
))]
#[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(C)] #[repr(C)]
pub struct MyRcd { pub struct MyRcd {
@ -57,6 +64,10 @@ mod test_gen_rs {
.unwrap_or_default(), .unwrap_or_default(),
indoc!( indoc!(
r#" r#"
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64"
))]
#[derive(Clone, Debug, Default, PartialEq, PartialOrd)] #[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
#[repr(C)] #[repr(C)]
pub struct Outer { pub struct Outer {
@ -65,12 +76,32 @@ mod test_gen_rs {
pub x: Inner, pub x: Inner,
} }
#[cfg(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "wasm32"
))]
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
#[repr(C)] #[repr(C)]
pub struct Inner { pub struct Inner {
pub b: f32, pub b: f32,
pub a: u16, pub a: u16,
} }
#[cfg(any(
target_arch = "x86",
target_arch = "arm",
target_arch = "wasm32"
))]
#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
#[repr(C)]
pub struct Outer {
pub x: Inner,
pub y: roc_std::RocStr,
pub z: roc_std::RocList<u8>,
}
"# "#
) )
); );
@ -86,6 +117,13 @@ mod test_gen_rs {
.unwrap_or_default(), .unwrap_or_default(),
indoc!( indoc!(
r#" r#"
#[cfg(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "wasm32"
))]
#[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(C)] #[repr(C)]
pub struct R1 { pub struct R1 {
@ -107,6 +145,10 @@ mod test_gen_rs {
.unwrap_or_default(), .unwrap_or_default(),
indoc!( indoc!(
r#" r#"
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64"
))]
#[derive(Clone, Debug, Default, PartialEq, PartialOrd)] #[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
#[repr(C)] #[repr(C)]
pub struct R1 { pub struct R1 {
@ -115,311 +157,32 @@ mod test_gen_rs {
pub x: R2, pub x: R2,
} }
#[cfg(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "wasm32"
))]
#[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)]
#[repr(C)] #[repr(C)]
pub struct R2 { pub struct R2 {
pub b: f32, pub b: f32,
pub a: u16, pub a: u16,
} }
"#
)
);
}
#[test]
fn tag_union_aliased() {
let module = indoc!(
r#"
NonRecursive : [Foo Str, Bar U128, Blah I32, Baz]
main : NonRecursive
main = Foo "blah"
"#
);
assert_eq!(
generate_bindings(module)
.strip_prefix('\n')
.unwrap_or_default(),
indoc!(
r#"
#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum tag_NonRecursive {
Bar = 0,
Baz = 1,
Blah = 2,
Foo = 3,
}
impl core::fmt::Debug for tag_NonRecursive {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Bar => f.write_str("tag_NonRecursive::Bar"),
Self::Baz => f.write_str("tag_NonRecursive::Baz"),
Self::Blah => f.write_str("tag_NonRecursive::Blah"),
Self::Foo => f.write_str("tag_NonRecursive::Foo"),
}
}
}
#[cfg(any(
target_arch = "x86",
target_arch = "arm",
target_arch = "wasm32"
))]
#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
#[repr(C)] #[repr(C)]
pub union NonRecursive { pub struct R1 {
Bar: roc_std::U128, pub x: R2,
Blah: i32, pub y: roc_std::RocStr,
Foo: core::mem::ManuallyDrop<roc_std::RocStr>, pub z: roc_std::RocList<u8>,
_size_with_discriminant: [u8; 32],
} }
impl NonRecursive {
pub fn tag(&self) -> tag_NonRecursive {
unsafe {
let bytes = core::mem::transmute::<&Self, &[u8; core::mem::size_of::<Self>()]>(self);
core::mem::transmute::<u8, tag_NonRecursive>(*bytes.as_ptr().add(24))
}
}
/// Internal helper
fn set_discriminant(&mut self, tag: tag_NonRecursive) {
let discriminant_ptr: *mut tag_NonRecursive = (self as *mut NonRecursive).cast();
unsafe {
*(discriminant_ptr.add(24)) = tag;
}
}
/// Construct a tag named Bar, with the appropriate payload
pub fn Bar(payload: roc_std::U128) -> Self {
let mut answer = Self {
Bar: payload
};
answer.set_discriminant(tag_NonRecursive::Bar);
answer
}
/// Unsafely assume the given NonRecursive has a .tag() of Bar and convert it to Bar's payload.
/// (Always examine .tag() first to make sure this is the correct variant!)
/// Panics in debug builds if the .tag() doesn't return Bar.
pub unsafe fn into_Bar(self) -> roc_std::U128 {
debug_assert_eq!(self.tag(), tag_NonRecursive::Bar);
self.Bar
}
/// Unsafely assume the given NonRecursive has a .tag() of Bar and return its payload.
/// (Always examine .tag() first to make sure this is the correct variant!)
/// Panics in debug builds if the .tag() doesn't return Bar.
pub unsafe fn as_Bar(&self) -> roc_std::U128 {
debug_assert_eq!(self.tag(), tag_NonRecursive::Bar);
self.Bar
}
/// A tag named Baz, which has no payload.
pub const Baz: Self = unsafe {
let mut bytes = [0; core::mem::size_of::<NonRecursive>()];
bytes[24] = tag_NonRecursive::Baz as u8;
core::mem::transmute::<[u8; core::mem::size_of::<NonRecursive>()], NonRecursive>(bytes)
};
/// Other `into_` methods return a payload, but since the Baz tag
/// has no payload, this does nothing and is only here for completeness.
pub fn into_Baz(self) {
()
}
/// Other `as` methods return a payload, but since the Baz tag
/// has no payload, this does nothing and is only here for completeness.
pub unsafe fn as_Baz(&self) {
()
}
/// Construct a tag named Blah, with the appropriate payload
pub fn Blah(payload: i32) -> Self {
let mut answer = Self {
Blah: payload
};
answer.set_discriminant(tag_NonRecursive::Blah);
answer
}
/// Unsafely assume the given NonRecursive has a .tag() of Blah and convert it to Blah's payload.
/// (Always examine .tag() first to make sure this is the correct variant!)
/// Panics in debug builds if the .tag() doesn't return Blah.
pub unsafe fn into_Blah(self) -> i32 {
debug_assert_eq!(self.tag(), tag_NonRecursive::Blah);
self.Blah
}
/// Unsafely assume the given NonRecursive has a .tag() of Blah and return its payload.
/// (Always examine .tag() first to make sure this is the correct variant!)
/// Panics in debug builds if the .tag() doesn't return Blah.
pub unsafe fn as_Blah(&self) -> i32 {
debug_assert_eq!(self.tag(), tag_NonRecursive::Blah);
self.Blah
}
/// Construct a tag named Foo, with the appropriate payload
pub fn Foo(payload: roc_std::RocStr) -> Self {
let mut answer = Self {
Foo: core::mem::ManuallyDrop::new(payload)
};
answer.set_discriminant(tag_NonRecursive::Foo);
answer
}
/// Unsafely assume the given NonRecursive has a .tag() of Foo and convert it to Foo's payload.
/// (Always examine .tag() first to make sure this is the correct variant!)
/// Panics in debug builds if the .tag() doesn't return Foo.
pub unsafe fn into_Foo(mut self) -> roc_std::RocStr {
debug_assert_eq!(self.tag(), tag_NonRecursive::Foo);
core::mem::ManuallyDrop::take(&mut self.Foo)
}
/// Unsafely assume the given NonRecursive has a .tag() of Foo and return its payload.
/// (Always examine .tag() first to make sure this is the correct variant!)
/// Panics in debug builds if the .tag() doesn't return Foo.
pub unsafe fn as_Foo(&self) -> &roc_std::RocStr {
debug_assert_eq!(self.tag(), tag_NonRecursive::Foo);
&self.Foo
}
}
impl Drop for NonRecursive {
fn drop(&mut self) {
match self.tag() {
tag_NonRecursive::Bar => {}
tag_NonRecursive::Baz => {}
tag_NonRecursive::Blah => {}
tag_NonRecursive::Foo => unsafe { core::mem::ManuallyDrop::drop(&mut self.Foo) },
}
}
}
impl PartialEq for NonRecursive {
fn eq(&self, other: &Self) -> bool {
if self.tag() != other.tag() {
return false;
}
unsafe {
match self.tag() {
tag_NonRecursive::Bar => self.Bar == other.Bar,
tag_NonRecursive::Baz => true,
tag_NonRecursive::Blah => self.Blah == other.Blah,
tag_NonRecursive::Foo => self.Foo == other.Foo,
}
}
}
}
impl Eq for NonRecursive {}
impl PartialOrd for NonRecursive {
fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
match self.tag().partial_cmp(&other.tag()) {
Some(core::cmp::Ordering::Equal) => {}
not_eq => return not_eq,
}
unsafe {
match self.tag() {
tag_NonRecursive::Bar => self.Bar.partial_cmp(&other.Bar),
tag_NonRecursive::Baz => Some(core::cmp::Ordering::Equal),
tag_NonRecursive::Blah => self.Blah.partial_cmp(&other.Blah),
tag_NonRecursive::Foo => self.Foo.partial_cmp(&other.Foo),
}
}
}
}
impl Ord for NonRecursive {
fn cmp(&self, other: &Self) -> core::cmp::Ordering {
match self.tag().cmp(&other.tag()) {
core::cmp::Ordering::Equal => {}
not_eq => return not_eq,
}
unsafe {
match self.tag() {
tag_NonRecursive::Bar => self.Bar.cmp(&other.Bar),
tag_NonRecursive::Baz => core::cmp::Ordering::Equal,
tag_NonRecursive::Blah => self.Blah.cmp(&other.Blah),
tag_NonRecursive::Foo => self.Foo.cmp(&other.Foo),
}
}
}
}
impl Clone for NonRecursive {
fn clone(&self) -> Self {
let mut answer = unsafe {
match self.tag() {
tag_NonRecursive::Bar => Self {
Bar: self.Bar.clone(),
},
tag_NonRecursive::Baz => core::mem::transmute::<
core::mem::MaybeUninit<NonRecursive>,
NonRecursive,
>(core::mem::MaybeUninit::uninit()),
tag_NonRecursive::Blah => Self {
Blah: self.Blah.clone(),
},
tag_NonRecursive::Foo => Self {
Foo: self.Foo.clone(),
},
}
};
answer.set_discriminant(self.tag());
answer
}
}
impl core::hash::Hash for NonRecursive {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
match self.tag() {
tag_NonRecursive::Bar => unsafe {
tag_NonRecursive::Bar.hash(state);
self.Bar.hash(state);
},
tag_NonRecursive::Baz => tag_NonRecursive::Baz.hash(state),
tag_NonRecursive::Blah => unsafe {
tag_NonRecursive::Blah.hash(state);
self.Blah.hash(state);
},
tag_NonRecursive::Foo => unsafe {
tag_NonRecursive::Foo.hash(state);
self.Foo.hash(state);
},
}
}
}
impl core::fmt::Debug for NonRecursive {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.write_str("NonRecursive::")?;
unsafe {
match self.tag() {
tag_NonRecursive::Bar => f.debug_tuple("Bar").field(&self.Bar).finish(),
tag_NonRecursive::Baz => f.write_str("Baz"),
tag_NonRecursive::Blah => f.debug_tuple("Blah").field(&self.Blah).finish(),
tag_NonRecursive::Foo => f.debug_tuple("Foo").field(&*self.Foo).finish(),
}
}
}
}
"# "#
) )
); );
@ -442,6 +205,13 @@ mod test_gen_rs {
.unwrap_or_default(), .unwrap_or_default(),
indoc!( indoc!(
r#" r#"
#[cfg(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "wasm32"
))]
#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)] #[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(u8)] #[repr(u8)]
pub enum Enumeration { pub enum Enumeration {
@ -481,12 +251,28 @@ mod test_gen_rs {
.unwrap_or_default(), .unwrap_or_default(),
indoc!( indoc!(
r#" r#"
#[cfg(any(
target_arch = "x86_64",
target_arch = "aarch64"
))]
#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] #[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(C)] #[repr(C)]
pub struct UserId { pub struct UserId {
pub f1: roc_std::RocStr, pub f1: roc_std::RocStr,
pub f0: u32, pub f0: u32,
} }
#[cfg(any(
target_arch = "x86",
target_arch = "arm",
target_arch = "wasm32"
))]
#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(C)]
pub struct UserId {
pub f0: u32,
pub f1: roc_std::RocStr,
}
"# "#
) )
); );
@ -509,6 +295,13 @@ mod test_gen_rs {
.unwrap_or_default(), .unwrap_or_default(),
indoc!( indoc!(
r#" r#"
#[cfg(any(
target_arch = "x86_64",
target_arch = "x86",
target_arch = "aarch64",
target_arch = "arm",
target_arch = "wasm32"
))]
#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)] #[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(transparent)] #[repr(transparent)]
pub struct UserId(roc_std::RocStr); pub struct UserId(roc_std::RocStr);
@ -516,276 +309,4 @@ mod test_gen_rs {
) )
); );
} }
#[test]
fn cons_list_of_strings() {
let module = indoc!(
r#"
StrConsList : [Nil, Cons Str StrConsList]
main : StrConsList
main = Cons "Hello, " (Cons "World!" Nil)
"#
);
assert_eq!(
generate_bindings(module)
.strip_prefix('\n')
.unwrap_or_default(),
indoc!(
r#"
#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum tag_StrConsList {
Cons = 0,
Nil = 1,
}
impl core::fmt::Debug for tag_StrConsList {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Cons => f.write_str("tag_StrConsList::Cons"),
Self::Nil => f.write_str("tag_StrConsList::Nil"),
}
}
}
#[derive(Clone, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(C)]
pub struct StrConsList {
pointer: *mut core::mem::ManuallyDrop<roc_std::RocStr>,
}
impl StrConsList {
pub fn tag(&self) -> tag_StrConsList {
if self.pointer.is_null() {
tag_StrConsList::Nil
} else {
tag_StrConsList::Cons
}
}
/// Construct a tag named Cons, with the appropriate payload
pub fn Cons(payload: roc_std::RocStr) -> Self {
let size = core::mem::size_of::<roc_std::RocStr>();
let align = core::mem::align_of::<roc_std::RocStr>();
unsafe {
let pointer = crate::roc_alloc(size, align as u32) as *mut core::mem::ManuallyDrop<roc_std::RocStr>;
*pointer = core::mem::ManuallyDrop::new(payload);
Self { pointer }
}
}
/// Unsafely assume the given StrConsList has a .tag() of Cons and convert it to Cons's payload.
/// (Always examine .tag() first to make sure this is the correct variant!)
/// Panics in debug builds if the .tag() doesn't return Cons.
pub unsafe fn into_Cons(self) -> roc_std::RocStr {
debug_assert_eq!(self.tag(), tag_StrConsList::Cons);
let payload = core::mem::ManuallyDrop::take(&mut *self.pointer);
let align = core::mem::align_of::<roc_std::RocStr>() as u32;
crate::roc_dealloc(self.pointer as *mut core::ffi::c_void, align);
payload
}
/// Unsafely assume the given StrConsList has a .tag() of Cons and return its payload.
/// (Always examine .tag() first to make sure this is the correct variant!)
/// Panics in debug builds if the .tag() doesn't return Cons.
pub unsafe fn as_Cons(&self) -> &roc_std::RocStr {
debug_assert_eq!(self.tag(), tag_StrConsList::Cons);
&*self.pointer
}
/// Construct a tag named Nil
pub fn Nil() -> Self {
Self {
pointer: core::ptr::null_mut(),
}
}
/// Other `into_` methods return a payload, but since the Nil tag
/// has no payload, this does nothing and is only here for completeness.
pub fn into_Nil(self) {
()
}
/// Other `as` methods return a payload, but since the Nil tag
/// has no payload, this does nothing and is only here for completeness.
pub unsafe fn as_Nil(&self) {
()
}
}
impl Drop for StrConsList {
fn drop(&mut self) {
if !self.pointer.is_null() {
let payload = unsafe { &*self.pointer };
let align = core::mem::align_of::<roc_std::RocStr>() as u32;
unsafe {
crate::roc_dealloc(self.pointer as *mut core::ffi::c_void, align);
}
drop(payload);
}
}
}
impl core::fmt::Debug for StrConsList {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.pointer.is_null() {
f.write_str("StrConsList::Nil")
} else {
f.write_str("StrConsList::")?;
unsafe { f.debug_tuple("Cons").field(&**self.pointer).finish() }
}
}
}
"#
)
);
}
#[test]
fn cons_list_of_ints() {
let module = indoc!(
r#"
IntConsList : [Empty, Prepend U16 IntConsList]
main : IntConsList
main = Prepend 42 (Prepend 26 Empty)
"#
);
assert_eq!(
generate_bindings(module)
.strip_prefix('\n')
.unwrap_or_default(),
indoc!(
r#"
#[derive(Clone, Copy, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(u8)]
pub enum tag_IntConsList {
Empty = 0,
Prepend = 1,
}
impl core::fmt::Debug for tag_IntConsList {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::Empty => f.write_str("tag_IntConsList::Empty"),
Self::Prepend => f.write_str("tag_IntConsList::Prepend"),
}
}
}
#[derive(Clone, Eq, Ord, Hash, PartialEq, PartialOrd)]
#[repr(C)]
pub struct IntConsList {
pointer: *mut u16,
}
impl IntConsList {
pub fn tag(&self) -> tag_IntConsList {
if self.pointer.is_null() {
tag_IntConsList::Empty
} else {
tag_IntConsList::Prepend
}
}
/// Construct a tag named Prepend, with the appropriate payload
pub fn Prepend(payload: u16) -> Self {
let size = core::mem::size_of::<u16>();
let align = core::mem::align_of::<u16>();
unsafe {
let pointer = crate::roc_alloc(size, align as u32) as *mut u16;
*pointer = payload;
Self { pointer }
}
}
/// Unsafely assume the given IntConsList has a .tag() of Prepend and convert it to Prepend's payload.
/// (Always examine .tag() first to make sure this is the correct variant!)
/// Panics in debug builds if the .tag() doesn't return Prepend.
pub unsafe fn into_Prepend(self) -> u16 {
debug_assert_eq!(self.tag(), tag_IntConsList::Prepend);
let payload = *self.pointer;
let align = core::mem::align_of::<u16>() as u32;
crate::roc_dealloc(self.pointer as *mut core::ffi::c_void, align);
payload
}
/// Unsafely assume the given IntConsList has a .tag() of Prepend and return its payload.
/// (Always examine .tag() first to make sure this is the correct variant!)
/// Panics in debug builds if the .tag() doesn't return Prepend.
pub unsafe fn as_Prepend(&self) -> u16 {
debug_assert_eq!(self.tag(), tag_IntConsList::Prepend);
*self.pointer
}
/// Construct a tag named Empty
pub fn Empty() -> Self {
Self {
pointer: core::ptr::null_mut(),
}
}
/// Other `into_` methods return a payload, but since the Empty tag
/// has no payload, this does nothing and is only here for completeness.
pub fn into_Empty(self) {
()
}
/// Other `as` methods return a payload, but since the Empty tag
/// has no payload, this does nothing and is only here for completeness.
pub unsafe fn as_Empty(&self) {
()
}
}
impl Drop for IntConsList {
fn drop(&mut self) {
if !self.pointer.is_null() {
let payload = unsafe { &*self.pointer };
let align = core::mem::align_of::<u16>() as u32;
unsafe {
crate::roc_dealloc(self.pointer as *mut core::ffi::c_void, align);
}
drop(payload);
}
}
}
impl core::fmt::Debug for IntConsList {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
if self.pointer.is_null() {
f.write_str("IntConsList::Empty")
} else {
f.write_str("IntConsList::")?;
unsafe { f.debug_tuple("Prepend").field(&*self.pointer).finish() }
}
}
}
"#
)
);
}
} }

View File

@ -25,7 +25,7 @@ pub fn generate_bindings(decl_src: &str) -> String {
src.push_str(decl_src); src.push_str(decl_src);
let types = { let pairs = {
let dir = tempdir().expect("Unable to create tempdir"); let dir = tempdir().expect("Unable to create tempdir");
let filename = PathBuf::from("Package-Config.roc"); let filename = PathBuf::from("Package-Config.roc");
let file_path = dir.path().join(filename); let file_path = dir.path().join(filename);
@ -40,13 +40,7 @@ pub fn generate_bindings(decl_src: &str) -> String {
result.expect("had problems loading") result.expect("had problems loading")
}; };
// Reuse the `src` allocation since we're done with it. bindgen_rs::emit(&pairs)
let mut buf = src;
buf.clear();
bindgen_rs::write_types(&types, &mut buf).expect("I/O error when writing bindgen string");
buf
} }
#[allow(dead_code)] #[allow(dead_code)]

View File

@ -60,11 +60,8 @@ impl FloatWidth {
match self { match self {
F32 => 4, F32 => 4,
F64 | F128 => match target_info.architecture { F64 | F128 => match target_info.architecture {
Architecture::X86_64 Architecture::X86_64 | Architecture::Aarch64 | Architecture::Wasm32 => 8,
| Architecture::Aarch64 Architecture::X86_32 | Architecture::Aarch32 => 4,
| Architecture::Arm
| Architecture::Wasm32 => 8,
Architecture::X86_32 => 4,
}, },
} }
} }
@ -129,7 +126,7 @@ impl IntWidth {
U64 | I64 => match target_info.architecture { U64 | I64 => match target_info.architecture {
Architecture::X86_64 Architecture::X86_64
| Architecture::Aarch64 | Architecture::Aarch64
| Architecture::Arm | Architecture::Aarch32
| Architecture::Wasm32 => 8, | Architecture::Wasm32 => 8,
Architecture::X86_32 => 4, Architecture::X86_32 => 4,
}, },

View File

@ -7,3 +7,5 @@ edition = "2021"
[dependencies] [dependencies]
target-lexicon = "0.12.3" target-lexicon = "0.12.3"
strum = "0.24.0"
strum_macros = "0.24"

View File

@ -2,6 +2,8 @@
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check. // See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
#![allow(clippy::large_enum_variant)] #![allow(clippy::large_enum_variant)]
use strum_macros::EnumIter;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct TargetInfo { pub struct TargetInfo {
pub architecture: Architecture, pub architecture: Architecture,
@ -50,6 +52,12 @@ impl From<&target_lexicon::Triple> for TargetInfo {
} }
} }
impl From<Architecture> for TargetInfo {
fn from(architecture: Architecture) -> Self {
Self { architecture }
}
}
#[repr(u8)] #[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PtrWidth { pub enum PtrWidth {
@ -57,12 +65,12 @@ pub enum PtrWidth {
Bytes8 = 8, Bytes8 = 8,
} }
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, PartialEq, Eq, EnumIter)]
pub enum Architecture { pub enum Architecture {
X86_64, X86_64,
X86_32, X86_32,
Aarch64, Aarch64,
Arm, Aarch32,
Wasm32, Wasm32,
} }
@ -71,8 +79,8 @@ impl Architecture {
use Architecture::*; use Architecture::*;
match self { match self {
X86_64 | Aarch64 | Arm => PtrWidth::Bytes8, X86_64 | Aarch64 => PtrWidth::Bytes8,
X86_32 | Wasm32 => PtrWidth::Bytes4, X86_32 | Aarch32 | Wasm32 => PtrWidth::Bytes4,
} }
} }
@ -87,7 +95,7 @@ impl From<target_lexicon::Architecture> for Architecture {
target_lexicon::Architecture::X86_64 => Architecture::X86_64, target_lexicon::Architecture::X86_64 => Architecture::X86_64,
target_lexicon::Architecture::X86_32(_) => Architecture::X86_32, target_lexicon::Architecture::X86_32(_) => Architecture::X86_32,
target_lexicon::Architecture::Aarch64(_) => Architecture::Aarch64, target_lexicon::Architecture::Aarch64(_) => Architecture::Aarch64,
target_lexicon::Architecture::Arm(_) => Architecture::Arm, target_lexicon::Architecture::Arm(_) => Architecture::Aarch32,
target_lexicon::Architecture::Wasm32 => Architecture::Wasm32, target_lexicon::Architecture::Wasm32 => Architecture::Wasm32,
_ => unreachable!("unsupported architecture"), _ => unreachable!("unsupported architecture"),
} }