mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-10 10:02:38 +03:00
Bindgen our first struct
This commit is contained in:
parent
131a633f7f
commit
ebcd72f7af
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -3439,6 +3439,7 @@ dependencies = [
|
||||
"bumpalo",
|
||||
"indoc",
|
||||
"pretty_assertions",
|
||||
"regex",
|
||||
"roc_builtins",
|
||||
"roc_can",
|
||||
"roc_collections",
|
||||
@ -3446,9 +3447,11 @@ dependencies = [
|
||||
"roc_load",
|
||||
"roc_module",
|
||||
"roc_mono",
|
||||
"roc_reporting",
|
||||
"roc_std",
|
||||
"roc_target",
|
||||
"roc_types",
|
||||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -21,3 +21,8 @@ bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
[dev-dependencies]
|
||||
pretty_assertions = "1.0.0"
|
||||
indoc = "1.0.3"
|
||||
bumpalo = { version = "3.8.0", features = ["collections"] }
|
||||
regex = "1.5.5"
|
||||
roc_load = { path = "../compiler/load" }
|
||||
roc_reporting = { path = "../reporting" }
|
||||
tempfile = "3.2.0"
|
||||
|
@ -3,10 +3,16 @@ use crate::structs::Structs;
|
||||
use crate::types::RocType;
|
||||
use bumpalo::Bump;
|
||||
use roc_collections::MutMap;
|
||||
use roc_module::ident::Lowercase;
|
||||
use roc_module::{
|
||||
ident::Lowercase,
|
||||
symbol::{Interns, Symbol},
|
||||
};
|
||||
use roc_mono::layout::{Layout, LayoutCache};
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::subs::{Content, FlatType, RecordFields, Subs, Variable};
|
||||
use roc_types::{
|
||||
subs::{Content, FlatType, RecordFields, Subs, Variable},
|
||||
types::RecordField,
|
||||
};
|
||||
|
||||
use std::{
|
||||
fmt::{self, Write},
|
||||
@ -81,9 +87,14 @@ pub fn write_roc_type(
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Env<'a> {
|
||||
pub arena: &'a Bump,
|
||||
pub layout_cache: &'a mut LayoutCache<'a>,
|
||||
pub interns: &'a Interns,
|
||||
}
|
||||
|
||||
pub fn write_layout_type<'a>(
|
||||
arena: &'a Bump,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
env: &mut Env<'a>,
|
||||
layout: Layout<'a>,
|
||||
content: &Content,
|
||||
subs: &Subs,
|
||||
@ -93,6 +104,14 @@ pub fn write_layout_type<'a>(
|
||||
use roc_builtins::bitcode::IntWidth::*;
|
||||
use roc_mono::layout::Builtin;
|
||||
|
||||
let (opt_name, content) = match content {
|
||||
Content::Alias(name, _variable, real_var, _kind) => {
|
||||
// todo handle type variables
|
||||
(Some(*name), subs.get_content_without_compacting(*real_var))
|
||||
}
|
||||
_ => (None, content),
|
||||
};
|
||||
|
||||
match layout {
|
||||
Layout::Builtin(builtin) => match builtin {
|
||||
Builtin::Int(width) => match width {
|
||||
@ -117,19 +136,19 @@ pub fn write_layout_type<'a>(
|
||||
Builtin::Str => buf.write_str("RocStr"),
|
||||
Builtin::Dict(key_layout, val_layout) => {
|
||||
buf.write_str("RocDict<")?;
|
||||
write_layout_type(arena, layout_cache, *key_layout, content, subs, buf)?;
|
||||
write_layout_type(env, *key_layout, content, subs, buf)?;
|
||||
buf.write_str(", ")?;
|
||||
write_layout_type(arena, layout_cache, *val_layout, content, subs, buf)?;
|
||||
write_layout_type(env, *val_layout, content, subs, buf)?;
|
||||
buf.write_char('>')
|
||||
}
|
||||
Builtin::Set(elem_type) => {
|
||||
buf.write_str("RocSet<")?;
|
||||
write_layout_type(arena, layout_cache, *elem_type, content, subs, buf)?;
|
||||
write_layout_type(env, *elem_type, content, subs, buf)?;
|
||||
buf.write_char('>')
|
||||
}
|
||||
Builtin::List(elem_type) => {
|
||||
buf.write_str("RocList<")?;
|
||||
write_layout_type(arena, layout_cache, *elem_type, content, subs, buf)?;
|
||||
write_layout_type(env, *elem_type, content, subs, buf)?;
|
||||
buf.write_char('>')
|
||||
}
|
||||
},
|
||||
@ -141,7 +160,7 @@ pub fn write_layout_type<'a>(
|
||||
Content::RigidAbleVar(_, _) => todo!(),
|
||||
Content::RecursionVar { .. } => todo!(),
|
||||
Content::Structure(FlatType::Record(fields, ext)) => {
|
||||
write_struct(arena, layout_cache, fields, *ext, subs, buf)
|
||||
write_struct(env, opt_name, fields, *ext, subs, buf)
|
||||
}
|
||||
Content::Structure(FlatType::TagUnion(tags, _)) => {
|
||||
debug_assert_eq!(tags.len(), 1);
|
||||
@ -191,29 +210,55 @@ pub fn write_layout_type<'a>(
|
||||
}
|
||||
|
||||
fn write_struct<'a>(
|
||||
arena: &'a Bump,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
env: &mut Env<'a>,
|
||||
opt_name: Option<Symbol>,
|
||||
record_fields: &RecordFields,
|
||||
ext: Variable,
|
||||
subs: &Subs,
|
||||
buf: &mut String,
|
||||
) -> fmt::Result {
|
||||
for (label, field) in record_fields.sorted_iterator(subs, ext) {
|
||||
// We recalculate the layouts here because we will have compiled the record so that its fields
|
||||
// are sorted by descending alignment, and then alphabetic, but the type of the record is
|
||||
// always only sorted alphabetically. We want to arrange the rendered record in the order of
|
||||
// the type.
|
||||
let field_content = subs.get_content_without_compacting(field.into_inner());
|
||||
let field_layout = layout_cache
|
||||
.from_var(arena, field.into_inner(), subs)
|
||||
.unwrap();
|
||||
let mut pairs = bumpalo::collections::Vec::with_capacity_in(record_fields.len(), env.arena);
|
||||
let it = record_fields
|
||||
.unsorted_iterator(subs, ext)
|
||||
.expect("something weird in content");
|
||||
for (label, field) in it {
|
||||
// drop optional fields
|
||||
let var = match field {
|
||||
RecordField::Optional(_) => continue,
|
||||
RecordField::Required(var) => var,
|
||||
RecordField::Demanded(var) => var,
|
||||
};
|
||||
|
||||
pairs.push((
|
||||
label,
|
||||
var,
|
||||
env.layout_cache.from_var(env.arena, var, subs).unwrap(),
|
||||
));
|
||||
}
|
||||
|
||||
pairs.sort_by(|(label1, _, layout1), (label2, _, layout2)| {
|
||||
let size1 = layout1.alignment_bytes(env.layout_cache.target_info);
|
||||
let size2 = layout2.alignment_bytes(env.layout_cache.target_info);
|
||||
|
||||
size2.cmp(&size1).then(label1.cmp(label2))
|
||||
});
|
||||
|
||||
let struct_name = opt_name
|
||||
.map(|sym| sym.as_str(env.interns))
|
||||
.unwrap_or("Unknown");
|
||||
buf.write_str("struct ");
|
||||
buf.write_str(struct_name);
|
||||
buf.write_str(" {\n");
|
||||
|
||||
for (label, field_var, field_layout) in pairs.into_iter() {
|
||||
let field_content = subs.get_content_without_compacting(field_var);
|
||||
|
||||
buf.write_str(INDENT)?;
|
||||
buf.write_str(label.as_str())?;
|
||||
buf.write_str(": ")?;
|
||||
write_layout_type(arena, layout_cache, field_layout, field_content, subs, buf)?;
|
||||
write_layout_type(env, field_layout, field_content, subs, buf)?;
|
||||
buf.write_str(",\n")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
buf.write_str("}\n")
|
||||
}
|
||||
|
@ -4,13 +4,142 @@ extern crate pretty_assertions;
|
||||
#[macro_use]
|
||||
extern crate indoc;
|
||||
|
||||
use bumpalo::Bump;
|
||||
use core::mem;
|
||||
use roc_bindgen::{
|
||||
bindgen_rs::write_roc_type,
|
||||
bindgen_rs::{write_layout_type, write_roc_type, Env},
|
||||
enums::Enums,
|
||||
structs::Structs,
|
||||
types::{self, RocType},
|
||||
};
|
||||
use roc_can::{
|
||||
def::{Declaration, Def},
|
||||
pattern::Pattern,
|
||||
};
|
||||
use roc_load::LoadedModule;
|
||||
use roc_mono::layout::LayoutCache;
|
||||
use roc_reporting::report::RenderTarget;
|
||||
use roc_target::TargetInfo;
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn run_load_and_typecheck(
|
||||
src: &str,
|
||||
target_info: TargetInfo,
|
||||
) -> Result<(LoadedModule), std::io::Error> {
|
||||
use bumpalo::Bump;
|
||||
use tempfile::tempdir;
|
||||
|
||||
let arena = &Bump::new();
|
||||
|
||||
assert!(
|
||||
src.starts_with("app"),
|
||||
"I need a module source, not an expr"
|
||||
);
|
||||
|
||||
let subs_by_module = Default::default();
|
||||
let loaded = {
|
||||
let dir = tempdir()?;
|
||||
let filename = PathBuf::from("Test.roc");
|
||||
let file_path = dir.path().join(filename);
|
||||
let full_file_path = file_path.clone();
|
||||
let mut file = File::create(file_path).unwrap();
|
||||
writeln!(file, "{}", &src).unwrap();
|
||||
let result = roc_load::load_and_typecheck(
|
||||
arena,
|
||||
full_file_path,
|
||||
dir.path(),
|
||||
subs_by_module,
|
||||
roc_target::TargetInfo::default_x86_64(),
|
||||
RenderTarget::Generic,
|
||||
);
|
||||
|
||||
dir.close()?;
|
||||
|
||||
result
|
||||
};
|
||||
|
||||
Ok(loaded.expect("had problems loading"))
|
||||
}
|
||||
|
||||
pub fn generate_bindings(src: &str, target_info: TargetInfo) -> String {
|
||||
let (LoadedModule {
|
||||
module_id: home,
|
||||
mut can_problems,
|
||||
mut type_problems,
|
||||
mut declarations_by_id,
|
||||
mut solved,
|
||||
interns,
|
||||
..
|
||||
}) = run_load_and_typecheck(src, target_info).expect("Something went wrong with IO");
|
||||
|
||||
let decls = declarations_by_id.remove(&home).unwrap();
|
||||
let subs = solved.inner_mut();
|
||||
|
||||
let can_problems = can_problems.remove(&home).unwrap_or_default();
|
||||
let type_problems = type_problems.remove(&home).unwrap_or_default();
|
||||
|
||||
if !can_problems.is_empty() || !type_problems.is_empty() {
|
||||
assert!(
|
||||
false,
|
||||
"There were problems: {:?}, {:?}",
|
||||
can_problems, type_problems
|
||||
);
|
||||
}
|
||||
|
||||
let arena = Bump::new();
|
||||
let mut layout_cache = LayoutCache::new(target_info);
|
||||
let mut env = Env {
|
||||
arena: &arena,
|
||||
layout_cache: &mut layout_cache,
|
||||
interns: &interns,
|
||||
};
|
||||
|
||||
let mut bindgen_result = String::new();
|
||||
|
||||
for decl in decls.into_iter() {
|
||||
let defs = match decl {
|
||||
Declaration::Declare(def) => {
|
||||
vec![def]
|
||||
}
|
||||
Declaration::DeclareRec(defs) => defs,
|
||||
Declaration::Builtin(..) => {
|
||||
unreachable!("Builtin decl in userspace module?")
|
||||
}
|
||||
Declaration::InvalidCycle(..) => {
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
for Def {
|
||||
loc_pattern,
|
||||
pattern_vars,
|
||||
..
|
||||
} in defs.into_iter()
|
||||
{
|
||||
match loc_pattern.value {
|
||||
Pattern::Identifier(sym) => {
|
||||
let var = pattern_vars
|
||||
.get(&sym)
|
||||
.expect("Indetifier known but it has no var?");
|
||||
let layout = env
|
||||
.layout_cache
|
||||
.from_var(&arena, *var, &subs)
|
||||
.expect("Something weird ended up in the content");
|
||||
let content = subs.get_content_without_compacting(*var);
|
||||
|
||||
write_layout_type(&mut env, layout, content, subs, &mut bindgen_result);
|
||||
}
|
||||
_ => {
|
||||
// figure out if we need to export non-identifier defs - when would that
|
||||
// happen?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bindgen_result
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn struct_without_different_pointer_alignment() {
|
||||
@ -40,3 +169,25 @@ fn struct_without_different_pointer_alignment() {
|
||||
out,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn my_struct_in_rust() {
|
||||
let module = r#"app "main" provides [ main ] to "./platform"
|
||||
|
||||
MyRcd : { a: U64, b: U128 }
|
||||
|
||||
main : MyRcd
|
||||
main = { a: 1u64, b: 2u128 }
|
||||
"#;
|
||||
|
||||
let bindings_rust = generate_bindings(module, TargetInfo::default_x86_64());
|
||||
|
||||
assert_eq!(
|
||||
bindings_rust,
|
||||
"struct MyRcd {
|
||||
b: u128,
|
||||
a: u64,
|
||||
}
|
||||
"
|
||||
);
|
||||
}
|
||||
|
@ -1328,7 +1328,7 @@ impl<'a> Layout<'a> {
|
||||
/// But if we're careful when to invalidate certain keys, we still get some benefit
|
||||
#[derive(Debug)]
|
||||
pub struct LayoutCache<'a> {
|
||||
target_info: TargetInfo,
|
||||
pub target_info: TargetInfo,
|
||||
_marker: std::marker::PhantomData<&'a u8>,
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user