Bindgen our first struct

This commit is contained in:
Ayaz Hafiz 2022-05-03 15:12:42 -04:00 committed by Richard Feldman
parent 131a633f7f
commit ebcd72f7af
No known key found for this signature in database
GPG Key ID: 7E4127D1E4241798
5 changed files with 228 additions and 24 deletions

3
Cargo.lock generated
View File

@ -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]]

View File

@ -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"

View File

@ -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")
}

View File

@ -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,
}
"
);
}

View File

@ -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>,
}