generate the RocFunction struct

This commit is contained in:
Folkert 2023-01-11 23:05:32 +01:00
parent 98ba49baf6
commit 6ead631c82
No known key found for this signature in database
GPG key ID: 1F17F6FFD112B97C
8 changed files with 266 additions and 63 deletions

View file

@ -5233,7 +5233,7 @@ fn build_host_exposed_alias_size_help<'a, 'ctx, 'env>(
builder.build_return(Some(&size));
}
pub fn build_proc<'a, 'ctx, 'env>(
fn build_proc<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
layout_interner: &mut STLayoutInterner<'a>,
mod_solutions: &'a ModSolutions,

View file

@ -10999,6 +10999,7 @@ pub fn generate_glue_procs<'a, I: Interner<'a, Layout<'a>>>(
}
},
Layout::LambdaSet(lambda_set) => {
// TODO generate closure caller
stack.push(lambda_set.runtime_representation(layout_interner))
}
Layout::RecursivePointer => {

View file

@ -29,6 +29,7 @@ strum_macros = "0.24"
indexmap = "1.8.1"
fnv = "1.0.7"
[dev-dependencies]
pretty_assertions = "1.3.0"
tempfile = "3.2.0"

View file

@ -290,9 +290,7 @@ fn add_type(target_info: TargetInfo, id: TypeId, types: &Types, impls: &mut Impl
// This is recursively pointing to a type that should already have been added,
// so no extra work needs to happen.
}
RocType::Function(RocFn { .. }) => {
// TODO actually generate glue functions!
}
RocType::Function(roc_fn) => add_function(target_info, roc_fn, types, impls),
}
}
@ -638,6 +636,7 @@ fn add_tag_union(
types: &Types,
impls: &mut Impls,
) {
dbg!("adding", name);
let name = escape_kw(name.to_string());
// We should never be attempting to generate glue for empty tag unions;
@ -1809,6 +1808,83 @@ fn add_enumeration<I: ExactSizeIterator<Item = S>, S: AsRef<str> + Display>(
add_decl(impls, None, target_info, buf);
}
fn add_function(
// name: &str,
target_info: TargetInfo,
roc_fn: &RocFn,
types: &Types,
impls: &mut Impls,
) {
let name = escape_kw(roc_fn.name.to_string());
// let derive = derive_str(types.get_type(struct_id), types, true);
let derive = "";
let pub_str = "pub ";
let mut buf;
let repr = "C";
buf = format!("{derive}\n#[repr({repr})]\n{pub_str}struct {name} {{\n");
let fields = [("closure_data", &roc_fn.lambda_set)];
for (label, type_id) in fields {
let type_str = type_name(*type_id, types);
// Tag union payloads have numbered fields, so we prefix them
// with an "f" because Rust doesn't allow struct fields to be numbers.
let label = escape_kw(label.to_string());
writeln!(buf, "{INDENT}pub {label}: {type_str},",).unwrap();
}
buf.push('}');
buf.push('\n');
buf.push('\n');
let arguments = "";
let argument_types = "";
let argument_names = "";
let extern_name = "roc__mainForHost_1__Fx2_caller";
let return_type_str = type_name(roc_fn.ret, types);
writeln!(buf, "impl {name} {{").unwrap();
writeln!(
buf,
"{INDENT}pub fn force_thunk(self, {arguments}) -> {return_type_str} {{"
)
.unwrap();
writeln!(buf, "{INDENT}{INDENT}extern \"C\" {{").unwrap();
writeln!(
buf,
"{INDENT}{INDENT}{INDENT} fn {extern_name}(output: *mut {return_type_str}, {argument_types});"
)
.unwrap();
writeln!(buf, "{INDENT}{INDENT}}}").unwrap();
writeln!(buf).unwrap();
writeln!(
buf,
"{INDENT}{INDENT}let mut output = std::mem::MaybeUninit::uninit();"
)
.unwrap();
writeln!(
buf,
"{INDENT}{INDENT}unsafe {{ {extern_name}(output.as_mut_ptr(), {argument_names}) }};"
)
.unwrap();
writeln!(buf, "{INDENT}{INDENT}unsafe {{ output.assume_init() }}").unwrap();
writeln!(buf, "{INDENT}}}").unwrap();
buf.push('}');
add_decl(impls, None, target_info, buf);
}
fn add_struct(
name: &str,
target_info: TargetInfo,

View file

@ -17,7 +17,7 @@ use roc_mono::layout::{
};
use roc_target::TargetInfo;
use roc_types::{
subs::{Content, FlatType, GetSubsSlice, Subs, UnionLabels, UnionTags, Variable},
subs::{Content, FlatType, GetSubsSlice, Label, Subs, UnionLabels, Variable},
types::{AliasKind, RecordField},
};
use std::fmt::Display;
@ -393,11 +393,13 @@ impl Types {
Function(RocFn {
name: name_a,
args: args_a,
lambda_set: lambda_a,
ret: ret_a,
}),
Function(RocFn {
name: name_b,
args: args_b,
lambda_set: lambda_b,
ret: ret_b,
}),
) => {
@ -405,6 +407,10 @@ impl Types {
// with the same type could have completely different implementations!
if name_a == name_b
&& args_a.len() == args_b.len()
&& self.is_equivalent_help(
self.get_type_or_pending(*lambda_a),
self.get_type_or_pending(*lambda_b),
)
&& self.is_equivalent_help(
self.get_type_or_pending(*ret_a),
self.get_type_or_pending(*ret_b),
@ -625,6 +631,7 @@ impl RocStructFields {
pub struct RocFn {
pub name: String,
pub args: Vec<TypeId>,
pub lambda_set: TypeId,
pub ret: TypeId,
}
@ -965,6 +972,15 @@ fn add_type_help<'a>(
arg_type_ids.push(add_type_help(env, arg_layout, *arg_var, None, types));
}
let lambda_set_type_id = {
let lambda_set_layout = env
.layout_cache
.from_var(env.arena, *closure_var, env.subs)
.expect("Something weird ended up in the content");
add_type_help(env, lambda_set_layout, *closure_var, None, types)
};
let ret_type_id = {
let ret_layout = env
.layout_cache
@ -975,16 +991,15 @@ fn add_type_help<'a>(
};
let name = format!("RocFunction_{:?}", closure_var);
let fn_type_id = types.add_named(
&env.layout_cache.interner,
name.clone(),
let fn_type_id = add_function(env, name, types, layout, |name| {
RocType::Function(RocFn {
name,
args: arg_type_ids.clone(),
lambda_set: lambda_set_type_id,
ret: ret_type_id,
}),
layout,
);
})
});
types.depends(fn_type_id, ret_type_id);
@ -1141,7 +1156,16 @@ fn add_type_help<'a>(
type_id
}
Content::LambdaSet(_) => todo!(),
Content::LambdaSet(lambda_set) => {
let tags = lambda_set.solved;
if tags.is_empty() {
// this function does not capture anything. Represent that at runtime as a unit value
types.add_anonymous(&env.layout_cache.interner, RocType::Unit, layout)
} else {
add_tag_union(env, opt_name, &tags, var, types, layout, None)
}
}
}
}
@ -1336,6 +1360,27 @@ fn add_builtin_type<'a>(
}
}
fn add_function<'a, F>(
env: &mut Env<'a>,
name: String,
types: &mut Types,
layout: Layout<'a>,
to_type: F,
) -> TypeId
where
F: FnOnce(String) -> RocType,
{
// let subs = env.subs;
// let arena = env.arena;
types.add_named(
&env.layout_cache.interner,
name.clone(),
to_type(name),
layout,
)
}
fn add_struct<'a, I, L, F>(
env: &mut Env<'a>,
name: String,
@ -1421,22 +1466,34 @@ where
)
}
fn add_tag_union<'a>(
trait UnionTag: Label + std::fmt::Debug {
fn union_tag_name(&self) -> String;
}
impl UnionTag for TagName {
fn union_tag_name(&self) -> String {
self.0.as_str().to_string()
}
}
impl UnionTag for Symbol {
fn union_tag_name(&self) -> String {
format!("C{:?}_{}", self.module_id(), self.ident_id().index())
}
}
fn tag_union_type_from_layout<'a>(
env: &mut Env<'a>,
opt_name: Option<Symbol>,
union_tags: &UnionTags,
name: String,
union_tags: &UnionLabels<impl UnionTag>,
var: Variable,
types: &mut Types,
layout: Layout<'a>,
rec_root: Option<Variable>,
) -> TypeId {
) -> RocTagUnion {
let subs = env.subs;
let name = match opt_name {
Some(sym) => sym.as_str(env.interns).to_string(),
None => env.enum_names.get_name(var),
};
let tag_union_type = match layout {
match layout {
_ if union_tags.is_newtype_wrapper(subs)
&& matches!(
subs.get_content_without_compacting(var),
@ -1453,7 +1510,7 @@ fn add_tag_union<'a>(
RocTagUnion::SingleTagStruct {
name: name.clone(),
tag_name: tag_name.to_string(),
tag_name,
payload,
}
}
@ -1509,7 +1566,7 @@ fn add_tag_union<'a>(
// e.g. `RoseTree a : [Tree a (List (RoseTree a))]`
RocTagUnion::NonNullableUnwrapped {
name: name.clone(),
tag_name: tag_name.to_string(),
tag_name,
payload: opt_payload.unwrap(),
}
}
@ -1620,20 +1677,51 @@ fn add_tag_union<'a>(
RocTagUnion::SingleTagStruct {
name: name.clone(),
tag_name: tag_name.to_string(),
tag_name,
payload: payload_fields,
}
}
Layout::LambdaSet(_) => {
todo!();
}
Layout::LambdaSet(lambda_set) => tag_union_type_from_layout(
env,
opt_name,
name,
union_tags,
var,
types,
lambda_set.runtime_representation(&env.layout_cache.interner),
),
Layout::RecursivePointer => {
// A single-tag union which only wraps itself is erroneous and should have
// been turned into an error earlier in the process.
unreachable!();
}
}
}
fn add_tag_union<'a>(
env: &mut Env<'a>,
opt_name: Option<Symbol>,
union_tags: &UnionLabels<impl UnionTag>,
var: Variable,
types: &mut Types,
layout: Layout<'a>,
rec_root: Option<Variable>,
) -> TypeId {
let name = match opt_name {
Some(sym) => sym.as_str(env.interns).to_string(),
None => env.enum_names.get_name(var),
};
let tag_union_type = tag_union_type_from_layout(
env,
opt_name,
name.to_string(),
union_tags,
var,
types,
layout,
);
let typ = RocType::TagUnion(tag_union_type);
let type_id = types.add_named(&env.layout_cache.interner, name, typ, layout);
@ -1645,14 +1733,14 @@ fn add_tag_union<'a>(
}
fn add_int_enumeration(
union_tags: &UnionLabels<TagName>,
union_tags: &UnionLabels<impl UnionTag>,
subs: &Subs,
name: &str,
int_width: IntWidth,
) -> RocTagUnion {
let tags: Vec<String> = union_tags
.iter_from_subs(subs)
.map(|(tag_name, _)| tag_name.0.as_str().to_string())
.map(|(tag_name, _)| tag_name.union_tag_name())
.collect();
RocTagUnion::Enumeration {
name: name.to_string(),
@ -1663,7 +1751,7 @@ fn add_int_enumeration(
fn union_tags_to_types<'a>(
name: &str,
union_tags: &UnionLabels<TagName>,
union_tags: &UnionLabels<impl UnionTag>,
subs: &Subs,
env: &mut Env<'a>,
types: &mut Types,
@ -1673,7 +1761,7 @@ fn union_tags_to_types<'a>(
let mut tags: Vec<(String, Vec<Variable>)> = union_tags
.iter_from_subs(subs)
.map(|(tag_name, payload_vars)| {
let name_str = tag_name.0.as_str().to_string();
let name_str = tag_name.union_tag_name();
(name_str, payload_vars.to_vec())
})
@ -1698,25 +1786,25 @@ fn union_tags_to_types<'a>(
}
fn single_tag_payload<'a>(
union_tags: &'a UnionLabels<TagName>,
union_tags: &'a UnionLabels<impl UnionTag>,
subs: &'a Subs,
) -> (&'a str, &'a [Variable]) {
) -> (String, &'a [Variable]) {
let mut iter = union_tags.iter_from_subs(subs);
let (tag_name, payload_vars) = iter.next().unwrap();
// This should be a single-tag union.
debug_assert_eq!(iter.next(), None);
debug_assert!(iter.next().is_none());
(tag_name.0.as_str(), payload_vars)
(tag_name.union_tag_name(), payload_vars)
}
fn single_tag_payload_fields<'a, 'b>(
union_tags: &'b UnionLabels<TagName>,
union_tags: &'b UnionLabels<impl UnionTag>,
subs: &'b Subs,
layout: Layout<'a>,
field_layouts: &[Layout<'a>],
env: &mut Env<'a>,
types: &mut Types,
) -> (&'b str, RocSingleTagPayload) {
) -> (String, RocSingleTagPayload) {
// There should be a glue_procs_by_layout entry iff this layout has a closure in it,
// so we shouldn't need to separately check that. Howeevr, we still do a debug_assert
// anyway just so we have some warning in case that relationship somehow didn't hold!

View file

@ -5,11 +5,13 @@ platform "echo-in-rust"
imports []
provides [mainForHost]
Op : [StdoutWrite Str ({} -> Op), StderrWrite Str ({} -> Op), Done]
# mainForHost : { bar: Str, foo: I64 -> I64 }
# mainForHost = { bar: main, foo: \x -> x }
mainForHost : Op
mainForHost : [StdoutWrite Str (({} -> Op) as Fx1), StderrWrite Str (({} -> Op) as Fx2), Done] as Op
mainForHost = main
# mainForHost : { x: Str, y: {} -> Str }
# mainForHost =
# y = "foo"
#
# when main is
# _ -> { x: "bar", y: \{} -> y }

View file

@ -86,6 +86,56 @@ union union_Op {
))]
//TODO HAS CLOSURE 2
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "wasm32",
target_arch = "x86",
target_arch = "x86_64"
))]
#[repr(C)]
pub struct RocFunction_65 {
pub closure_data: (),
}
impl RocFunction_65 {
pub fn force_thunk(self, ) -> Op {
extern "C" {
fn roc__mainForHost_1__Fx2_caller(output: *mut Op, );
}
let mut output = std::mem::MaybeUninit::uninit();
unsafe { roc__mainForHost_1__Fx2_caller(output.as_mut_ptr(), ) };
unsafe { output.assume_init() }
}
}
#[cfg(any(
target_arch = "arm",
target_arch = "aarch64",
target_arch = "wasm32",
target_arch = "x86",
target_arch = "x86_64"
))]
#[repr(C)]
pub struct RocFunction_67 {
pub closure_data: (),
}
impl RocFunction_67 {
pub fn force_thunk(self, ) -> Op {
extern "C" {
fn roc__mainForHost_1__Fx2_caller(output: *mut Op, );
}
let mut output = std::mem::MaybeUninit::uninit();
unsafe { roc__mainForHost_1__Fx2_caller(output.as_mut_ptr(), ) };
unsafe { output.assume_init() }
}
}
#[cfg(any(
target_arch = "aarch64",
target_arch = "x86_64"
@ -97,21 +147,6 @@ union union_Op {
_sizer: [u8; 8],
}
#[derive(Clone)]
struct RocFunction_3 {
closure_data: T
}
impl RocFunction_3 {
pub fn call(self) -> U {
extern "C" {
fn call_the_closure(T) -> U ;
}
call_the_closure(self.closure_data)
}
}
impl Op {
#[cfg(any(
target_arch = "arm",
@ -224,12 +259,12 @@ impl Op {
/// Unsafely assume this `Op` has a `.discriminant()` of `StderrWrite` and return its payload at index 1.
/// (Always examine `.discriminant()` first to make sure this is the correct variant!)
/// Panics in debug builds if the `.discriminant()` doesn't return `StderrWrite`.
pub unsafe fn get_StderrWrite_1(&self) -> TODO_roc_function_69 {
pub unsafe fn get_StderrWrite_1(&self) -> RocFunction_67 {
debug_assert_eq!(self.discriminant(), discriminant_Op::StderrWrite);
extern "C" {
#[link_name = "roc__getter__3"]
fn getter(_: *mut TODO_roc_function_69, _: *const Op);
fn getter(_: *mut RocFunction_67, _: *const Op);
}
let mut ret = core::mem::MaybeUninit::uninit();
@ -346,12 +381,12 @@ impl Op {
/// Unsafely assume this `Op` has a `.discriminant()` of `StdoutWrite` and return its payload at index 1.
/// (Always examine `.discriminant()` first to make sure this is the correct variant!)
/// Panics in debug builds if the `.discriminant()` doesn't return `StdoutWrite`.
pub unsafe fn get_StdoutWrite_1(&self) -> TODO_roc_function_70 {
pub unsafe fn get_StdoutWrite_1(&self) -> RocFunction_65 {
debug_assert_eq!(self.discriminant(), discriminant_Op::StdoutWrite);
extern "C" {
#[link_name = "roc__getter__3"]
fn getter(_: *mut TODO_roc_function_70, _: *const Op);
fn getter(_: *mut RocFunction_65, _: *const Op);
}
let mut ret = core::mem::MaybeUninit::uninit();

View file

@ -105,7 +105,7 @@ pub extern "C" fn rust_main() -> i32 {
match dbg!(op.discriminant()) {
StdoutWrite => {
let output: RocStr = unsafe { op.get_StdoutWrite_0() };
op = unsafe { op.get_StdoutWrite_1() };
op = unsafe { op.get_StdoutWrite_1().force_thunk() };
dbg!(&output);