mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 05:34:11 +03:00
Merge pull request #3128 from rtfeldman/multi-arch-bindgen
Multi-arch bindgen
This commit is contained in:
commit
8bcc815661
5
Cargo.lock
generated
5
Cargo.lock
generated
@ -3459,6 +3459,7 @@ dependencies = [
|
||||
"cli_utils",
|
||||
"ctor",
|
||||
"dircpy",
|
||||
"indexmap",
|
||||
"indoc",
|
||||
"pretty_assertions",
|
||||
"roc_builtins",
|
||||
@ -3473,6 +3474,8 @@ dependencies = [
|
||||
"roc_target",
|
||||
"roc_test_utils",
|
||||
"roc_types",
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"target-lexicon",
|
||||
"tempfile",
|
||||
]
|
||||
@ -4113,6 +4116,8 @@ dependencies = [
|
||||
name = "roc_target"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"strum",
|
||||
"strum_macros",
|
||||
"target-lexicon",
|
||||
]
|
||||
|
||||
|
@ -28,6 +28,9 @@ roc_error_macros = { path = "../error_macros" }
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
target-lexicon = "0.12.3"
|
||||
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]
|
||||
pretty_assertions = "1.0.0"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -8,15 +8,17 @@ use roc_can::{
|
||||
use roc_load::{LoadedModule, Threading};
|
||||
use roc_mono::layout::LayoutCache;
|
||||
use roc_reporting::report::RenderTarget;
|
||||
use roc_target::Architecture;
|
||||
use std::io;
|
||||
use std::path::{Path, PathBuf};
|
||||
use strum::IntoEnumIterator;
|
||||
use target_lexicon::Triple;
|
||||
|
||||
pub fn load_types(
|
||||
full_file_path: PathBuf,
|
||||
dir: &Path,
|
||||
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
|
||||
// depending on 32-bit vs 64-bit targets.
|
||||
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 {
|
||||
arena,
|
||||
layout_cache: &mut layout_cache,
|
||||
@ -65,27 +70,24 @@ pub fn load_types(
|
||||
enum_names: Default::default(),
|
||||
subs,
|
||||
};
|
||||
|
||||
let mut types = Types::default();
|
||||
|
||||
for decl in decls.into_iter() {
|
||||
for decl in decls.iter() {
|
||||
let defs = match decl {
|
||||
Declaration::Declare(def) => {
|
||||
vec![def]
|
||||
vec![def.clone()]
|
||||
}
|
||||
Declaration::DeclareRec(defs, cycle_mark) => {
|
||||
if cycle_mark.is_illegal(subs) {
|
||||
vec![]
|
||||
Vec::new()
|
||||
} else {
|
||||
defs
|
||||
defs.clone()
|
||||
}
|
||||
}
|
||||
Declaration::Builtin(..) => {
|
||||
unreachable!("Builtin decl in userspace module?")
|
||||
}
|
||||
Declaration::InvalidCycle(..) => {
|
||||
vec![]
|
||||
}
|
||||
Declaration::InvalidCycle(..) => Vec::new(),
|
||||
};
|
||||
|
||||
for Def {
|
||||
@ -107,5 +109,8 @@ pub fn load_types(
|
||||
}
|
||||
}
|
||||
|
||||
Ok(types)
|
||||
answer.push((architecture, types));
|
||||
}
|
||||
|
||||
Ok(answer)
|
||||
}
|
||||
|
@ -57,30 +57,21 @@ pub fn main() {
|
||||
};
|
||||
|
||||
match load_types(input_path.clone(), &cwd, Threading::AllAvailable) {
|
||||
Ok(types) => {
|
||||
Ok(types_by_architecture) => {
|
||||
let mut buf;
|
||||
|
||||
let result = match output_type {
|
||||
match output_type {
|
||||
OutputType::Rust => {
|
||||
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::Zig => todo!("TODO: Generate bindings for Zig"),
|
||||
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| {
|
||||
eprintln!(
|
||||
"Unable to create output file {} - {:?}",
|
||||
|
@ -9,7 +9,7 @@ use std::convert::TryInto;
|
||||
#[derive(Copy, Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct TypeId(usize);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct Types {
|
||||
by_id: Vec<RocType>,
|
||||
|
||||
|
@ -1,8 +1,10 @@
|
||||
mod bindings;
|
||||
|
||||
use bindings::NonRecursive;
|
||||
|
||||
extern "C" {
|
||||
#[link_name = "roc__mainForHost_1_exposed_generic"]
|
||||
fn roc_main(_: *mut bindings::NonRecursive);
|
||||
fn roc_main(_: *mut NonRecursive);
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
@ -11,8 +13,7 @@ pub extern "C" fn rust_main() -> i32 {
|
||||
use std::collections::hash_set::HashSet;
|
||||
|
||||
let tag_union = unsafe {
|
||||
let mut ret: core::mem::MaybeUninit<bindings::NonRecursive> =
|
||||
core::mem::MaybeUninit::uninit();
|
||||
let mut ret: core::mem::MaybeUninit<NonRecursive> = core::mem::MaybeUninit::uninit();
|
||||
|
||||
roc_main(ret.as_mut_ptr());
|
||||
|
||||
@ -30,11 +31,11 @@ pub extern "C" fn rust_main() -> i32 {
|
||||
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,
|
||||
bindings::NonRecursive::Foo("small str".into()),
|
||||
bindings::NonRecursive::Foo("A long enough string to not be small".into()),
|
||||
bindings::NonRecursive::Bar(123.into()),
|
||||
bindings::NonRecursive::Baz,
|
||||
bindings::NonRecursive::Blah(456),
|
||||
NonRecursive::Foo("small str".into()),
|
||||
NonRecursive::Foo("A long enough string to not be small".into()),
|
||||
NonRecursive::Bar(123.into()),
|
||||
NonRecursive::Baz,
|
||||
NonRecursive::Blah(456),
|
||||
); // Debug
|
||||
|
||||
let mut set = HashSet::new();
|
||||
|
@ -27,6 +27,13 @@ mod test_gen_rs {
|
||||
.unwrap_or_default(),
|
||||
indoc!(
|
||||
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)]
|
||||
#[repr(C)]
|
||||
pub struct MyRcd {
|
||||
@ -57,6 +64,10 @@ mod test_gen_rs {
|
||||
.unwrap_or_default(),
|
||||
indoc!(
|
||||
r#"
|
||||
#[cfg(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64"
|
||||
))]
|
||||
#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
|
||||
#[repr(C)]
|
||||
pub struct Outer {
|
||||
@ -65,12 +76,32 @@ mod test_gen_rs {
|
||||
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)]
|
||||
#[repr(C)]
|
||||
pub struct Inner {
|
||||
pub b: f32,
|
||||
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(),
|
||||
indoc!(
|
||||
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)]
|
||||
#[repr(C)]
|
||||
pub struct R1 {
|
||||
@ -107,6 +145,10 @@ mod test_gen_rs {
|
||||
.unwrap_or_default(),
|
||||
indoc!(
|
||||
r#"
|
||||
#[cfg(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64"
|
||||
))]
|
||||
#[derive(Clone, Debug, Default, PartialEq, PartialOrd)]
|
||||
#[repr(C)]
|
||||
pub struct R1 {
|
||||
@ -115,311 +157,32 @@ mod test_gen_rs {
|
||||
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)]
|
||||
#[repr(C)]
|
||||
pub struct R2 {
|
||||
pub b: f32,
|
||||
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)]
|
||||
pub union NonRecursive {
|
||||
Bar: roc_std::U128,
|
||||
Blah: i32,
|
||||
Foo: core::mem::ManuallyDrop<roc_std::RocStr>,
|
||||
_size_with_discriminant: [u8; 32],
|
||||
pub struct R1 {
|
||||
pub x: R2,
|
||||
pub y: roc_std::RocStr,
|
||||
pub z: roc_std::RocList<u8>,
|
||||
}
|
||||
|
||||
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(),
|
||||
indoc!(
|
||||
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)]
|
||||
#[repr(u8)]
|
||||
pub enum Enumeration {
|
||||
@ -481,12 +251,28 @@ mod test_gen_rs {
|
||||
.unwrap_or_default(),
|
||||
indoc!(
|
||||
r#"
|
||||
#[cfg(any(
|
||||
target_arch = "x86_64",
|
||||
target_arch = "aarch64"
|
||||
))]
|
||||
#[derive(Clone, Debug, Default, Eq, Ord, Hash, PartialEq, PartialOrd)]
|
||||
#[repr(C)]
|
||||
pub struct UserId {
|
||||
pub f1: roc_std::RocStr,
|
||||
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(),
|
||||
indoc!(
|
||||
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)]
|
||||
#[repr(transparent)]
|
||||
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() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"#
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ pub fn generate_bindings(decl_src: &str) -> String {
|
||||
|
||||
src.push_str(decl_src);
|
||||
|
||||
let types = {
|
||||
let pairs = {
|
||||
let dir = tempdir().expect("Unable to create tempdir");
|
||||
let filename = PathBuf::from("Package-Config.roc");
|
||||
let file_path = dir.path().join(filename);
|
||||
@ -40,13 +40,7 @@ pub fn generate_bindings(decl_src: &str) -> String {
|
||||
result.expect("had problems loading")
|
||||
};
|
||||
|
||||
// Reuse the `src` allocation since we're done with it.
|
||||
let mut buf = src;
|
||||
buf.clear();
|
||||
|
||||
bindgen_rs::write_types(&types, &mut buf).expect("I/O error when writing bindgen string");
|
||||
|
||||
buf
|
||||
bindgen_rs::emit(&pairs)
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
|
@ -60,11 +60,8 @@ impl FloatWidth {
|
||||
match self {
|
||||
F32 => 4,
|
||||
F64 | F128 => match target_info.architecture {
|
||||
Architecture::X86_64
|
||||
| Architecture::Aarch64
|
||||
| Architecture::Arm
|
||||
| Architecture::Wasm32 => 8,
|
||||
Architecture::X86_32 => 4,
|
||||
Architecture::X86_64 | Architecture::Aarch64 | Architecture::Wasm32 => 8,
|
||||
Architecture::X86_32 | Architecture::Aarch32 => 4,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -129,7 +126,7 @@ impl IntWidth {
|
||||
U64 | I64 => match target_info.architecture {
|
||||
Architecture::X86_64
|
||||
| Architecture::Aarch64
|
||||
| Architecture::Arm
|
||||
| Architecture::Aarch32
|
||||
| Architecture::Wasm32 => 8,
|
||||
Architecture::X86_32 => 4,
|
||||
},
|
||||
|
@ -7,3 +7,5 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
target-lexicon = "0.12.3"
|
||||
strum = "0.24.0"
|
||||
strum_macros = "0.24"
|
||||
|
@ -2,6 +2,8 @@
|
||||
// See github.com/rtfeldman/roc/issues/800 for discussion of the large_enum_variant check.
|
||||
#![allow(clippy::large_enum_variant)]
|
||||
|
||||
use strum_macros::EnumIter;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct TargetInfo {
|
||||
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)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum PtrWidth {
|
||||
@ -57,12 +65,12 @@ pub enum PtrWidth {
|
||||
Bytes8 = 8,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, EnumIter)]
|
||||
pub enum Architecture {
|
||||
X86_64,
|
||||
X86_32,
|
||||
Aarch64,
|
||||
Arm,
|
||||
Aarch32,
|
||||
Wasm32,
|
||||
}
|
||||
|
||||
@ -71,8 +79,8 @@ impl Architecture {
|
||||
use Architecture::*;
|
||||
|
||||
match self {
|
||||
X86_64 | Aarch64 | Arm => PtrWidth::Bytes8,
|
||||
X86_32 | Wasm32 => PtrWidth::Bytes4,
|
||||
X86_64 | Aarch64 => PtrWidth::Bytes8,
|
||||
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_32(_) => Architecture::X86_32,
|
||||
target_lexicon::Architecture::Aarch64(_) => Architecture::Aarch64,
|
||||
target_lexicon::Architecture::Arm(_) => Architecture::Arm,
|
||||
target_lexicon::Architecture::Arm(_) => Architecture::Aarch32,
|
||||
target_lexicon::Architecture::Wasm32 => Architecture::Wasm32,
|
||||
_ => unreachable!("unsupported architecture"),
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user