mirror of
https://github.com/roc-lang/roc.git
synced 2025-08-03 11:52:19 +00:00
remove Boxed layout
This commit is contained in:
parent
d64930c17f
commit
6d2d65bb1e
24 changed files with 52 additions and 1377 deletions
|
@ -37,7 +37,6 @@ pub fn eq_generic<'a>(
|
|||
Builtin(List(elem_layout)) => eq_list(root, ident_ids, ctx, layout_interner, elem_layout),
|
||||
Struct(field_layouts) => eq_struct(root, ident_ids, ctx, layout_interner, field_layouts),
|
||||
Union(union_layout) => eq_tag_union(root, ident_ids, ctx, layout_interner, union_layout),
|
||||
Boxed(inner_layout) => eq_boxed(root, ident_ids, ctx, layout_interner, inner_layout),
|
||||
Ptr(inner_layout) => eq_boxed(root, ident_ids, ctx, layout_interner, inner_layout),
|
||||
LambdaSet(_) => unreachable!("`==` is not defined on functions"),
|
||||
RecursivePointer(_) => {
|
||||
|
|
|
@ -571,11 +571,6 @@ impl<'a> CodeGenHelp<'a> {
|
|||
return layout;
|
||||
}
|
||||
|
||||
LayoutRepr::Boxed(inner) => {
|
||||
let inner = self.replace_rec_ptr(ctx, layout_interner, inner);
|
||||
LayoutRepr::Boxed(inner)
|
||||
}
|
||||
|
||||
LayoutRepr::Ptr(inner) => {
|
||||
let inner = self.replace_rec_ptr(ctx, layout_interner, inner);
|
||||
LayoutRepr::Ptr(inner)
|
||||
|
@ -842,7 +837,6 @@ fn layout_needs_helper_proc<'a>(
|
|||
LayoutRepr::Union(_) => true,
|
||||
LayoutRepr::LambdaSet(_) => true,
|
||||
LayoutRepr::RecursivePointer(_) => false,
|
||||
LayoutRepr::Boxed(_) => true,
|
||||
LayoutRepr::Ptr(_) => false,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -233,66 +233,12 @@ pub fn refcount_generic<'a>(
|
|||
LayoutRepr::RecursivePointer(_) => unreachable!(
|
||||
"We should never call a refcounting helper on a RecursivePointer layout directly"
|
||||
),
|
||||
LayoutRepr::Boxed(inner_layout) => refcount_boxed(
|
||||
root,
|
||||
ident_ids,
|
||||
ctx,
|
||||
layout_interner,
|
||||
layout,
|
||||
inner_layout,
|
||||
structure,
|
||||
),
|
||||
LayoutRepr::Ptr(_) => {
|
||||
unreachable!("We should never call a refcounting helper on a Ptr layout directly")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn if_unique<'a>(
|
||||
root: &mut CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
value: Symbol,
|
||||
when_unique: impl FnOnce(JoinPointId) -> Stmt<'a>,
|
||||
when_done: Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
// joinpoint f =
|
||||
// <when_done>
|
||||
// in
|
||||
// if is_unique <value> then
|
||||
// <when_unique>(f)
|
||||
// else
|
||||
// jump f
|
||||
|
||||
let joinpoint = root.create_symbol(ident_ids, "is_unique_joinpoint");
|
||||
let joinpoint = JoinPointId(joinpoint);
|
||||
|
||||
let is_unique = root.create_symbol(ident_ids, "is_unique");
|
||||
|
||||
let mut stmt = Stmt::if_then_else(
|
||||
root.arena,
|
||||
is_unique,
|
||||
Layout::UNIT,
|
||||
when_unique(joinpoint),
|
||||
root.arena.alloc(Stmt::Jump(joinpoint, &[])),
|
||||
);
|
||||
|
||||
stmt = Stmt::Join {
|
||||
id: joinpoint,
|
||||
parameters: &[],
|
||||
body: root.arena.alloc(when_done),
|
||||
remainder: root.arena.alloc(stmt),
|
||||
};
|
||||
|
||||
let_lowlevel(
|
||||
root.arena,
|
||||
root.layout_isize,
|
||||
is_unique,
|
||||
LowLevel::RefCountIsUnique,
|
||||
&[value],
|
||||
root.arena.alloc(stmt),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn refcount_reset_proc_body<'a>(
|
||||
root: &mut CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
|
@ -1978,72 +1924,3 @@ fn refcount_tag_fields<'a>(
|
|||
|
||||
stmt
|
||||
}
|
||||
|
||||
fn refcount_boxed<'a>(
|
||||
root: &mut CodeGenHelp<'a>,
|
||||
ident_ids: &mut IdentIds,
|
||||
ctx: &mut Context<'a>,
|
||||
layout_interner: &mut STLayoutInterner<'a>,
|
||||
layout: InLayout<'a>,
|
||||
inner_layout: InLayout<'a>,
|
||||
outer: Symbol,
|
||||
) -> Stmt<'a> {
|
||||
let arena = root.arena;
|
||||
|
||||
//
|
||||
// modify refcount of the inner and outer structures
|
||||
// RC on inner first, to avoid use-after-free for Dec
|
||||
// We're defining statements in reverse, so define outer first
|
||||
//
|
||||
|
||||
let alignment = layout_interner.allocation_alignment_bytes(layout);
|
||||
let ret_stmt = rc_return_stmt(root, ident_ids, ctx);
|
||||
let modify_outer = modify_refcount(
|
||||
root,
|
||||
ident_ids,
|
||||
ctx,
|
||||
Pointer::ToData(outer),
|
||||
alignment,
|
||||
arena.alloc(ret_stmt),
|
||||
);
|
||||
|
||||
// decrement the inner value if the operation is a decrement and the box itself is unique
|
||||
if layout_interner.is_refcounted(inner_layout) && ctx.op.is_dec() {
|
||||
let inner = root.create_symbol(ident_ids, "inner");
|
||||
let inner_expr = Expr::ptr_load(arena.alloc(outer));
|
||||
|
||||
let mod_inner_unit = root.create_symbol(ident_ids, "mod_inner_unit");
|
||||
let mod_inner_args = refcount_args(root, ctx, inner);
|
||||
let mod_inner_expr = root
|
||||
.call_specialized_op(
|
||||
ident_ids,
|
||||
ctx,
|
||||
layout_interner,
|
||||
inner_layout,
|
||||
mod_inner_args,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if_unique(
|
||||
root,
|
||||
ident_ids,
|
||||
outer,
|
||||
|id| {
|
||||
Stmt::Let(
|
||||
inner,
|
||||
inner_expr,
|
||||
inner_layout,
|
||||
arena.alloc(Stmt::Let(
|
||||
mod_inner_unit,
|
||||
mod_inner_expr,
|
||||
LAYOUT_UNIT,
|
||||
arena.alloc(Stmt::Jump(id, &[])),
|
||||
)),
|
||||
)
|
||||
},
|
||||
modify_outer,
|
||||
)
|
||||
} else {
|
||||
modify_outer
|
||||
}
|
||||
}
|
||||
|
|
|
@ -526,15 +526,6 @@ fn specialize_drops_stmt<'a, 'i>(
|
|||
&mut incremented_children,
|
||||
continuation,
|
||||
),
|
||||
LayoutRepr::Boxed(_layout) => specialize_boxed(
|
||||
arena,
|
||||
layout_interner,
|
||||
ident_ids,
|
||||
environment,
|
||||
&mut incremented_children,
|
||||
symbol,
|
||||
continuation,
|
||||
),
|
||||
LayoutRepr::Builtin(Builtin::List(layout)) => specialize_list(
|
||||
arena,
|
||||
layout_interner,
|
||||
|
@ -1059,65 +1050,6 @@ fn specialize_union<'a, 'i>(
|
|||
}
|
||||
}
|
||||
|
||||
fn specialize_boxed<'a, 'i>(
|
||||
arena: &'a Bump,
|
||||
layout_interner: &'i mut STLayoutInterner<'a>,
|
||||
ident_ids: &'i mut IdentIds,
|
||||
environment: &mut DropSpecializationEnvironment<'a>,
|
||||
incremented_children: &mut CountingMap<Child>,
|
||||
symbol: &Symbol,
|
||||
continuation: &'a Stmt<'a>,
|
||||
) -> &'a Stmt<'a> {
|
||||
let removed = match incremented_children.map.iter().next() {
|
||||
Some((s, _)) => {
|
||||
let s = *s;
|
||||
incremented_children.pop(&s);
|
||||
Some(s)
|
||||
}
|
||||
None => None,
|
||||
};
|
||||
|
||||
let new_continuation =
|
||||
specialize_drops_stmt(arena, layout_interner, ident_ids, environment, continuation);
|
||||
|
||||
match removed {
|
||||
Some(s) => {
|
||||
branch_uniqueness(
|
||||
arena,
|
||||
ident_ids,
|
||||
layout_interner,
|
||||
environment,
|
||||
*symbol,
|
||||
// If the symbol is unique:
|
||||
// - free the box
|
||||
|_, _, continuation| {
|
||||
arena.alloc(Stmt::Refcounting(
|
||||
// we know for sure that the allocation is unique at
|
||||
// this point. Therefore we can free (or maybe reuse)
|
||||
// without checking the refcount again.
|
||||
ModifyRc::Free(*symbol),
|
||||
continuation,
|
||||
))
|
||||
},
|
||||
// If the symbol is not unique:
|
||||
// - increment the child
|
||||
// - decref the box
|
||||
|_, _, continuation| {
|
||||
arena.alloc(Stmt::Refcounting(
|
||||
ModifyRc::Inc(s, 1),
|
||||
arena.alloc(Stmt::Refcounting(ModifyRc::DecRef(*symbol), continuation)),
|
||||
))
|
||||
},
|
||||
new_continuation,
|
||||
)
|
||||
}
|
||||
None => {
|
||||
// No known children, keep decrementing the symbol.
|
||||
arena.alloc(Stmt::Refcounting(ModifyRc::Dec(*symbol), new_continuation))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn specialize_list<'a, 'i>(
|
||||
arena: &'a Bump,
|
||||
layout_interner: &'i mut STLayoutInterner<'a>,
|
||||
|
|
|
@ -9915,9 +9915,6 @@ where
|
|||
stack.push(layout_interner.get(*in_layout));
|
||||
}
|
||||
}
|
||||
LayoutRepr::Boxed(boxed) => {
|
||||
stack.push(layout_interner.get(boxed));
|
||||
}
|
||||
LayoutRepr::Ptr(inner) => {
|
||||
stack.push(layout_interner.get(inner));
|
||||
}
|
||||
|
@ -9989,7 +9986,7 @@ where
|
|||
{
|
||||
let interned_unboxed_struct_layout = layout_interner.insert(*unboxed_struct_layout);
|
||||
let boxed_struct_layout =
|
||||
Layout::no_semantic(LayoutRepr::Boxed(interned_unboxed_struct_layout).direct());
|
||||
Layout::no_semantic(LayoutRepr::Ptr(interned_unboxed_struct_layout).direct());
|
||||
let boxed_struct_layout = layout_interner.insert(boxed_struct_layout);
|
||||
let mut answer = bumpalo::collections::Vec::with_capacity_in(field_layouts.len(), arena);
|
||||
|
||||
|
@ -10100,7 +10097,7 @@ where
|
|||
I: LayoutInterner<'a>,
|
||||
{
|
||||
let interned = layout_interner.insert(*unboxed_struct_layout);
|
||||
let boxed_struct_layout = Layout::no_semantic(LayoutRepr::Boxed(interned).direct());
|
||||
let boxed_struct_layout = Layout::no_semantic(LayoutRepr::Ptr(interned).direct());
|
||||
let boxed_struct_layout = layout_interner.insert(boxed_struct_layout);
|
||||
let mut answer = bumpalo::collections::Vec::with_capacity_in(field_layouts.len(), arena);
|
||||
|
||||
|
@ -10130,7 +10127,6 @@ where
|
|||
let field_get_stmt = Stmt::Let(result, field_get_expr, *field, ret_stmt);
|
||||
|
||||
let unbox_expr = Expr::ptr_load(arena.alloc(argument));
|
||||
|
||||
let unbox_stmt = Stmt::Let(unboxed, unbox_expr, interned, arena.alloc(field_get_stmt));
|
||||
|
||||
let proc = Proc {
|
||||
|
|
|
@ -674,8 +674,6 @@ pub(crate) enum LayoutWrapper<'a> {
|
|||
pub enum LayoutRepr<'a> {
|
||||
Builtin(Builtin<'a>),
|
||||
Struct(&'a [InLayout<'a>]),
|
||||
// A (heap allocated) reference-counted value
|
||||
Boxed(InLayout<'a>),
|
||||
// A pointer (heap or stack) without any reference counting
|
||||
// Ptr is not user-facing. The compiler author must make sure that invariants are upheld
|
||||
Ptr(InLayout<'a>),
|
||||
|
@ -2534,7 +2532,7 @@ impl<'a> LayoutRepr<'a> {
|
|||
pub const F64: Self = LayoutRepr::Builtin(Builtin::Float(FloatWidth::F64));
|
||||
pub const DEC: Self = LayoutRepr::Builtin(Builtin::Decimal);
|
||||
pub const STR: Self = LayoutRepr::Builtin(Builtin::Str);
|
||||
pub const OPAQUE_PTR: Self = LayoutRepr::Boxed(Layout::VOID);
|
||||
pub const OPAQUE_PTR: Self = LayoutRepr::Ptr(Layout::VOID);
|
||||
|
||||
pub const fn struct_(field_layouts: &'a [InLayout<'a>]) -> Self {
|
||||
Self::Struct(field_layouts)
|
||||
|
@ -2576,7 +2574,7 @@ impl<'a> LayoutRepr<'a> {
|
|||
LambdaSet(lambda_set) => interner
|
||||
.get_repr(lambda_set.runtime_representation())
|
||||
.safe_to_memcpy(interner),
|
||||
Boxed(_) | Ptr(_) | RecursivePointer(_) => {
|
||||
Ptr(_) | RecursivePointer(_) => {
|
||||
// We cannot memcpy pointers, because then we would have the same pointer in multiple places!
|
||||
false
|
||||
}
|
||||
|
@ -2666,7 +2664,6 @@ impl<'a> LayoutRepr<'a> {
|
|||
.get_repr(lambda_set.runtime_representation())
|
||||
.stack_size_without_alignment(interner),
|
||||
RecursivePointer(_) => interner.target_info().ptr_width() as u32,
|
||||
Boxed(_) => interner.target_info().ptr_width() as u32,
|
||||
Ptr(_) => interner.target_info().ptr_width() as u32,
|
||||
}
|
||||
}
|
||||
|
@ -2720,7 +2717,6 @@ impl<'a> LayoutRepr<'a> {
|
|||
.alignment_bytes(interner),
|
||||
Builtin(builtin) => builtin.alignment_bytes(interner.target_info()),
|
||||
RecursivePointer(_) => interner.target_info().ptr_width() as u32,
|
||||
Boxed(_) => interner.target_info().ptr_width() as u32,
|
||||
Ptr(_) => interner.target_info().ptr_width() as u32,
|
||||
}
|
||||
}
|
||||
|
@ -2742,10 +2738,6 @@ impl<'a> LayoutRepr<'a> {
|
|||
RecursivePointer(_) => {
|
||||
unreachable!("should be looked up to get an actual layout")
|
||||
}
|
||||
Boxed(inner) => Ord::max(
|
||||
ptr_width,
|
||||
interner.get_repr(*inner).alignment_bytes(interner),
|
||||
),
|
||||
Ptr(inner) => interner.get_repr(*inner).alignment_bytes(interner),
|
||||
}
|
||||
}
|
||||
|
@ -2815,7 +2807,6 @@ impl<'a> LayoutRepr<'a> {
|
|||
.get_repr(lambda_set.runtime_representation())
|
||||
.contains_refcounted(interner),
|
||||
RecursivePointer(_) => true,
|
||||
Boxed(_) => true,
|
||||
Ptr(_) => {
|
||||
// we never consider pointers for refcounting. Ptr is not user-facing. The compiler
|
||||
// author must make sure that invariants are upheld
|
||||
|
@ -2876,7 +2867,7 @@ impl<'a> LayoutRepr<'a> {
|
|||
}
|
||||
},
|
||||
LambdaSet(_) => return true,
|
||||
Boxed(_) | Ptr(_) => {
|
||||
Ptr(_) => {
|
||||
// If there's any layer of indirection (behind a pointer), then it doesn't vary!
|
||||
}
|
||||
RecursivePointer(_) => {
|
||||
|
|
|
@ -76,7 +76,7 @@ cache_interned_layouts! {
|
|||
16, STR, pub, nosema!(LayoutRepr::STR)
|
||||
17, OPAQUE_PTR, pub, nosema!(LayoutRepr::OPAQUE_PTR)
|
||||
18, NAKED_RECURSIVE_PTR, pub(super), nosema!(LayoutRepr::RecursivePointer(Layout::VOID))
|
||||
19, STR_PTR, pub, nosema!(LayoutRepr::Boxed(Layout::STR))
|
||||
19, STR_PTR, pub, nosema!(LayoutRepr::Ptr(Layout::STR))
|
||||
20, LIST_U8, pub, nosema!(LayoutRepr::Builtin(crate::layout::Builtin::List(Layout::U8)))
|
||||
|
||||
; 21
|
||||
|
@ -350,10 +350,6 @@ pub trait LayoutInterner<'a>: Sized {
|
|||
self.to_doc(rec_layout, alloc, seen_rec, parens)
|
||||
}
|
||||
}
|
||||
Boxed(inner) => alloc
|
||||
.text("Boxed(")
|
||||
.append(self.to_doc(inner, alloc, seen_rec, parens))
|
||||
.append(")"),
|
||||
Ptr(inner) => alloc
|
||||
.text("Ptr(")
|
||||
.append(self.to_doc(inner, alloc, seen_rec, parens))
|
||||
|
@ -1115,7 +1111,6 @@ mod reify {
|
|||
LayoutRepr::Struct(field_layouts) => {
|
||||
LayoutRepr::Struct(reify_layout_slice(arena, interner, slot, field_layouts))
|
||||
}
|
||||
LayoutRepr::Boxed(lay) => LayoutRepr::Boxed(reify_layout(arena, interner, slot, lay)),
|
||||
LayoutRepr::Ptr(lay) => LayoutRepr::Ptr(reify_layout(arena, interner, slot, lay)),
|
||||
LayoutRepr::Union(un) => LayoutRepr::Union(reify_union(arena, interner, slot, un)),
|
||||
LayoutRepr::LambdaSet(ls) => {
|
||||
|
@ -1320,7 +1315,6 @@ mod equiv {
|
|||
(Struct(fl1), Struct(fl2)) => {
|
||||
equiv_fields!(fl1, fl2)
|
||||
}
|
||||
(Boxed(b1), Boxed(b2)) => stack.push((b1, b2)),
|
||||
(Ptr(b1), Ptr(b2)) => stack.push((b1, b2)),
|
||||
(Union(u1), Union(u2)) => {
|
||||
use UnionLayout::*;
|
||||
|
@ -1441,7 +1435,6 @@ pub mod dbg_deep {
|
|||
.debug_struct("Struct")
|
||||
.field("fields", &DbgFields(self.0, field_layouts))
|
||||
.finish(),
|
||||
LayoutRepr::Boxed(b) => f.debug_tuple("Boxed").field(&Dbg(self.0, *b)).finish(),
|
||||
LayoutRepr::Ptr(b) => f.debug_tuple("Ptr").field(&Dbg(self.0, *b)).finish(),
|
||||
LayoutRepr::Union(un) => f
|
||||
.debug_tuple("Union")
|
||||
|
@ -1615,7 +1608,6 @@ pub mod dbg_stable {
|
|||
.debug_struct("Struct")
|
||||
.field("fields", &DbgFields(self.0, field_layouts))
|
||||
.finish(),
|
||||
LayoutRepr::Boxed(b) => f.debug_tuple("Boxed").field(&Dbg(self.0, *b)).finish(),
|
||||
LayoutRepr::Ptr(b) => f.debug_tuple("Ptr").field(&Dbg(self.0, *b)).finish(),
|
||||
LayoutRepr::Union(un) => f
|
||||
.debug_tuple("Union")
|
||||
|
|
|
@ -1,907 +0,0 @@
|
|||
use crate::layout::{ext_var_is_empty_record, ext_var_is_empty_tag_union};
|
||||
use roc_builtins::bitcode::{FloatWidth, IntWidth};
|
||||
use roc_collections::all::MutMap;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_target::TargetInfo;
|
||||
use roc_types::subs::{self, Content, FlatType, Subs, Variable};
|
||||
use roc_types::types::RecordField;
|
||||
use std::collections::hash_map::Entry;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Index<T> {
|
||||
index: u32,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Index<T> {
|
||||
pub const fn new(index: u32) -> Self {
|
||||
Self {
|
||||
index,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub struct Slice<T> {
|
||||
start: u32,
|
||||
length: u16,
|
||||
_marker: std::marker::PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Slice<T> {
|
||||
pub const fn new(start: u32, length: u16) -> Self {
|
||||
Self {
|
||||
start,
|
||||
length,
|
||||
_marker: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub const fn len(&self) -> usize {
|
||||
self.length as _
|
||||
}
|
||||
|
||||
pub const fn is_empty(&self) -> bool {
|
||||
self.length == 0
|
||||
}
|
||||
|
||||
pub const fn indices(&self) -> std::ops::Range<usize> {
|
||||
self.start as usize..(self.start as usize + self.length as usize)
|
||||
}
|
||||
|
||||
pub fn into_iter(&self) -> impl Iterator<Item = Index<T>> {
|
||||
self.indices().map(|i| Index::new(i as _))
|
||||
}
|
||||
}
|
||||
|
||||
trait Reserve {
|
||||
fn reserve(layouts: &mut Layouts, length: usize) -> Self;
|
||||
}
|
||||
|
||||
impl Reserve for Slice<Layout> {
|
||||
fn reserve(layouts: &mut Layouts, length: usize) -> Self {
|
||||
let start = layouts.layouts.len() as u32;
|
||||
|
||||
let it = std::iter::repeat(Layout::Reserved).take(length);
|
||||
layouts.layouts.extend(it);
|
||||
|
||||
Self {
|
||||
start,
|
||||
length: length as u16,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Reserve for Slice<Slice<Layout>> {
|
||||
fn reserve(layouts: &mut Layouts, length: usize) -> Self {
|
||||
let start = layouts.layout_slices.len() as u32;
|
||||
|
||||
let empty: Slice<Layout> = Slice::new(0, 0);
|
||||
let it = std::iter::repeat(empty).take(length);
|
||||
layouts.layout_slices.extend(it);
|
||||
|
||||
Self {
|
||||
start,
|
||||
length: length as u16,
|
||||
_marker: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static_assertions::assert_eq_size!([u8; 12], Layout);
|
||||
|
||||
pub struct Layouts {
|
||||
layouts: Vec<Layout>,
|
||||
layout_slices: Vec<Slice<Layout>>,
|
||||
// function_layouts: Vec<(Slice<Layout>, Index<LambdaSet>)>,
|
||||
lambda_sets: Vec<LambdaSet>,
|
||||
symbols: Vec<Symbol>,
|
||||
recursion_variable_to_structure_variable_map: MutMap<Variable, Index<Layout>>,
|
||||
target_info: TargetInfo,
|
||||
}
|
||||
|
||||
pub struct FunctionLayout {
|
||||
/// last element is the result, prior elements the arguments
|
||||
arguments_and_result: Slice<Layout>,
|
||||
pub lambda_set: Index<LambdaSet>,
|
||||
}
|
||||
|
||||
impl FunctionLayout {
|
||||
pub fn from_var(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
) -> Result<Self, LayoutError> {
|
||||
// so we can set some things/clean up
|
||||
Self::from_var_help(layouts, subs, var)
|
||||
}
|
||||
|
||||
fn from_var_help(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
) -> Result<Self, LayoutError> {
|
||||
let content = &subs.get_content_without_compacting(var);
|
||||
Self::from_content(layouts, subs, var, content)
|
||||
}
|
||||
|
||||
fn from_content(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
content: &Content,
|
||||
) -> Result<Self, LayoutError> {
|
||||
use LayoutError::*;
|
||||
|
||||
match content {
|
||||
Content::FlexVar(_)
|
||||
| Content::RigidVar(_)
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _) => Err(UnresolvedVariable(var)),
|
||||
Content::RecursionVar { .. } => Err(TypeError(())),
|
||||
Content::LambdaSet(lset) => Self::from_lambda_set(layouts, subs, *lset),
|
||||
Content::Structure(flat_type) => Self::from_flat_type(layouts, subs, flat_type),
|
||||
Content::Alias(_, _, actual, _) => Self::from_var_help(layouts, subs, *actual),
|
||||
Content::RangedNumber(_) => todo!(),
|
||||
Content::Error => Err(TypeError(())),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_lambda_set(
|
||||
_layouts: &mut Layouts,
|
||||
_subs: &Subs,
|
||||
_lset: subs::LambdaSet,
|
||||
) -> Result<Self, LayoutError> {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn from_flat_type(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
flat_type: &FlatType,
|
||||
) -> Result<Self, LayoutError> {
|
||||
match flat_type {
|
||||
FlatType::Func(arguments, lambda_set, result) => {
|
||||
let slice = Slice::reserve(layouts, arguments.len() + 1);
|
||||
|
||||
let variable_slice = &subs.variables[arguments.indices()];
|
||||
let it = slice.indices().zip(variable_slice);
|
||||
for (target_index, var) in it {
|
||||
let layout = Layout::from_var_help(layouts, subs, *var)?;
|
||||
layouts.layouts[target_index] = layout;
|
||||
}
|
||||
|
||||
let result_layout = Layout::from_var_help(layouts, subs, *result)?;
|
||||
let result_index: Index<Layout> = Index::new(slice.start + slice.len() as u32 - 1);
|
||||
layouts.layouts[result_index.index as usize] = result_layout;
|
||||
|
||||
let lambda_set = LambdaSet::from_var(layouts, subs, *lambda_set)?;
|
||||
let lambda_set_index = Index::new(layouts.lambda_sets.len() as u32);
|
||||
layouts.lambda_sets.push(lambda_set);
|
||||
|
||||
Ok(Self {
|
||||
arguments_and_result: slice,
|
||||
lambda_set: lambda_set_index,
|
||||
})
|
||||
}
|
||||
|
||||
_ => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn argument_slice(&self) -> Slice<Layout> {
|
||||
let mut result = self.arguments_and_result;
|
||||
result.length -= 1;
|
||||
|
||||
result
|
||||
}
|
||||
pub fn result_index(&self) -> Index<Layout> {
|
||||
Index::new(self.arguments_and_result.start + self.arguments_and_result.length as u32 - 1)
|
||||
}
|
||||
}
|
||||
|
||||
/// Idea: don't include the symbols for the first 3 cases in --optimize mode
|
||||
pub enum LambdaSet {
|
||||
Empty {
|
||||
symbol: Index<Symbol>,
|
||||
},
|
||||
Single {
|
||||
symbol: Index<Symbol>,
|
||||
layout: Index<Layout>,
|
||||
},
|
||||
Struct {
|
||||
symbol: Index<Symbol>,
|
||||
layouts: Slice<Layout>,
|
||||
},
|
||||
Union {
|
||||
symbols: Slice<Symbol>,
|
||||
layouts: Slice<Slice<Layout>>,
|
||||
},
|
||||
}
|
||||
|
||||
impl LambdaSet {
|
||||
pub fn from_var(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
) -> Result<Self, LayoutError> {
|
||||
// so we can set some things/clean up
|
||||
Self::from_var_help(layouts, subs, var)
|
||||
}
|
||||
|
||||
fn from_var_help(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
) -> Result<Self, LayoutError> {
|
||||
let content = &subs.get_content_without_compacting(var);
|
||||
Self::from_content(layouts, subs, var, content)
|
||||
}
|
||||
|
||||
fn from_content(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
content: &Content,
|
||||
) -> Result<Self, LayoutError> {
|
||||
use LayoutError::*;
|
||||
|
||||
match content {
|
||||
Content::FlexVar(_)
|
||||
| Content::RigidVar(_)
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _) => Err(UnresolvedVariable(var)),
|
||||
Content::RecursionVar { .. } => {
|
||||
unreachable!("lambda sets cannot currently be recursive")
|
||||
}
|
||||
Content::LambdaSet(lset) => Self::from_lambda_set(layouts, subs, *lset),
|
||||
Content::Structure(_flat_type) => unreachable!(),
|
||||
Content::Alias(_, _, actual, _) => Self::from_var_help(layouts, subs, *actual),
|
||||
Content::RangedNumber(_) => todo!(),
|
||||
Content::Error => Err(TypeError(())),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_lambda_set(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
lset: subs::LambdaSet,
|
||||
) -> Result<Self, LayoutError> {
|
||||
let subs::LambdaSet {
|
||||
solved,
|
||||
recursion_var: _,
|
||||
unspecialized: _,
|
||||
ambient_function: _,
|
||||
} = lset;
|
||||
|
||||
// TODO: handle unspecialized
|
||||
|
||||
debug_assert!(
|
||||
!solved.is_empty(),
|
||||
"lambda set must contain atleast the function itself"
|
||||
);
|
||||
|
||||
let lambda_names = solved.labels();
|
||||
let closure_names = Self::get_closure_names(layouts, subs, lambda_names);
|
||||
|
||||
let variables = solved.variables();
|
||||
if variables.len() == 1 {
|
||||
let symbol = subs.symbol_names[lambda_names.start as usize];
|
||||
let symbol_index = Index::new(layouts.symbols.len() as u32);
|
||||
layouts.symbols.push(symbol);
|
||||
let variable_slice = subs.variable_slices[variables.start as usize];
|
||||
|
||||
match variable_slice.len() {
|
||||
0 => Ok(LambdaSet::Empty {
|
||||
symbol: symbol_index,
|
||||
}),
|
||||
1 => {
|
||||
let var = subs.variables[variable_slice.start as usize];
|
||||
let layout = Layout::from_var(layouts, subs, var)?;
|
||||
|
||||
let index = Index::new(layouts.layouts.len() as u32);
|
||||
layouts.layouts.push(layout);
|
||||
|
||||
Ok(LambdaSet::Single {
|
||||
symbol: symbol_index,
|
||||
layout: index,
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
let slice = Layout::from_variable_slice(layouts, subs, variable_slice)?;
|
||||
|
||||
Ok(LambdaSet::Struct {
|
||||
symbol: symbol_index,
|
||||
layouts: slice,
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let layouts = Layout::from_slice_variable_slice(layouts, subs, solved.variables())?;
|
||||
|
||||
Ok(LambdaSet::Union {
|
||||
symbols: closure_names,
|
||||
layouts,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn get_closure_names(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
subs_slice: roc_types::subs::SubsSlice<Symbol>,
|
||||
) -> Slice<Symbol> {
|
||||
let slice = Slice::new(layouts.symbols.len() as u32, subs_slice.len() as u16);
|
||||
|
||||
let symbols = &subs.symbol_names[subs_slice.indices()];
|
||||
|
||||
for symbol in symbols {
|
||||
layouts.symbols.push(*symbol);
|
||||
}
|
||||
|
||||
slice
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub enum Layout {
|
||||
// theory: we can zero out memory to reserve space for many layouts
|
||||
Reserved,
|
||||
|
||||
// Question: where to store signedness information?
|
||||
Int(IntWidth),
|
||||
Float(FloatWidth),
|
||||
Decimal,
|
||||
|
||||
Str,
|
||||
Dict(Index<(Layout, Layout)>),
|
||||
Set(Index<Layout>),
|
||||
List(Index<Layout>),
|
||||
|
||||
Struct(Slice<Layout>),
|
||||
|
||||
UnionNonRecursive(Slice<Slice<Layout>>),
|
||||
|
||||
Boxed(Index<Layout>),
|
||||
UnionRecursive(Slice<Slice<Layout>>),
|
||||
// UnionNonNullableUnwrapped(Slice<Layout>),
|
||||
// UnionNullableWrapper {
|
||||
// data: NullableUnionIndex,
|
||||
// tag_id: u16,
|
||||
// },
|
||||
//
|
||||
// UnionNullableUnwrappedTrue(Slice<Layout>),
|
||||
// UnionNullableUnwrappedFalse(Slice<Layout>),
|
||||
|
||||
// RecursivePointer,
|
||||
}
|
||||
|
||||
fn round_up_to_alignment(unaligned: u16, alignment_bytes: u16) -> u16 {
|
||||
let unaligned = unaligned as i32;
|
||||
let alignment_bytes = alignment_bytes as i32;
|
||||
if alignment_bytes <= 1 {
|
||||
return unaligned as u16;
|
||||
}
|
||||
if alignment_bytes.count_ones() != 1 {
|
||||
panic!(
|
||||
"Cannot align to {} bytes. Not a power of 2.",
|
||||
alignment_bytes
|
||||
);
|
||||
}
|
||||
let mut aligned = unaligned;
|
||||
aligned += alignment_bytes - 1; // if lower bits are non-zero, push it over the next boundary
|
||||
aligned &= -alignment_bytes; // mask with a flag that has upper bits 1, lower bits 0
|
||||
|
||||
aligned as u16
|
||||
}
|
||||
|
||||
impl Layouts {
|
||||
const VOID_INDEX: Index<Layout> = Index::new(0);
|
||||
const VOID_TUPLE: Index<(Layout, Layout)> = Index::new(0);
|
||||
const UNIT_INDEX: Index<Layout> = Index::new(2);
|
||||
|
||||
pub fn new(target_info: TargetInfo) -> Self {
|
||||
let mut layouts = Vec::with_capacity(64);
|
||||
|
||||
layouts.push(Layout::VOID);
|
||||
layouts.push(Layout::VOID);
|
||||
layouts.push(Layout::UNIT);
|
||||
|
||||
// sanity check
|
||||
debug_assert_eq!(layouts[Self::VOID_INDEX.index as usize], Layout::VOID);
|
||||
debug_assert_eq!(layouts[Self::VOID_TUPLE.index as usize + 1], Layout::VOID);
|
||||
debug_assert_eq!(layouts[Self::UNIT_INDEX.index as usize], Layout::UNIT);
|
||||
|
||||
Layouts {
|
||||
layouts: Vec::default(),
|
||||
layout_slices: Vec::default(),
|
||||
lambda_sets: Vec::default(),
|
||||
symbols: Vec::default(),
|
||||
recursion_variable_to_structure_variable_map: MutMap::default(),
|
||||
target_info,
|
||||
}
|
||||
}
|
||||
|
||||
/// sort a slice according to elements' alignment
|
||||
fn sort_slice_by_alignment(&mut self, layout_slice: Slice<Layout>) {
|
||||
let slice = &mut self.layouts[layout_slice.indices()];
|
||||
|
||||
// SAFETY: the align_of function does not mutate the layouts vector
|
||||
// this unsafety is required to circumvent the borrow checker
|
||||
let sneaky_slice =
|
||||
unsafe { std::slice::from_raw_parts_mut(slice.as_mut_ptr(), slice.len()) };
|
||||
|
||||
sneaky_slice.sort_by(|layout1, layout2| {
|
||||
let align1 = self.align_of_layout(*layout1);
|
||||
let align2 = self.align_of_layout(*layout2);
|
||||
|
||||
// we want the biggest alignment first
|
||||
align2.cmp(&align1)
|
||||
});
|
||||
}
|
||||
|
||||
fn usize(&self) -> Layout {
|
||||
let usize_int_width = match self.target_info.ptr_width() {
|
||||
roc_target::PtrWidth::Bytes4 => IntWidth::U32,
|
||||
roc_target::PtrWidth::Bytes8 => IntWidth::U64,
|
||||
};
|
||||
|
||||
Layout::Int(usize_int_width)
|
||||
}
|
||||
|
||||
fn align_of_layout_index(&self, index: Index<Layout>) -> u16 {
|
||||
let layout = self.layouts[index.index as usize];
|
||||
|
||||
self.align_of_layout(layout)
|
||||
}
|
||||
|
||||
fn align_of_layout(&self, layout: Layout) -> u16 {
|
||||
let usize_int_width = match self.target_info.ptr_width() {
|
||||
roc_target::PtrWidth::Bytes4 => IntWidth::U32,
|
||||
roc_target::PtrWidth::Bytes8 => IntWidth::U64,
|
||||
};
|
||||
|
||||
let ptr_alignment = usize_int_width.alignment_bytes(self.target_info) as u16;
|
||||
|
||||
match layout {
|
||||
Layout::Reserved => unreachable!(),
|
||||
Layout::Int(int_width) => int_width.alignment_bytes(self.target_info) as u16,
|
||||
Layout::Float(float_width) => float_width.alignment_bytes(self.target_info) as u16,
|
||||
Layout::Decimal => IntWidth::U128.alignment_bytes(self.target_info) as u16,
|
||||
Layout::Str | Layout::Dict(_) | Layout::Set(_) | Layout::List(_) => ptr_alignment,
|
||||
Layout::Struct(slice) => self.align_of_layout_slice(slice),
|
||||
Layout::Boxed(_) | Layout::UnionRecursive(_) => ptr_alignment,
|
||||
Layout::UnionNonRecursive(slices) => {
|
||||
let tag_id_align = IntWidth::I64.alignment_bytes(self.target_info) as u16;
|
||||
|
||||
self.align_of_layout_slices(slices).max(tag_id_align)
|
||||
}
|
||||
// Layout::UnionNonNullableUnwrapped(_) => todo!(),
|
||||
// Layout::UnionNullableWrapper { data, tag_id } => todo!(),
|
||||
// Layout::UnionNullableUnwrappedTrue(_) => todo!(),
|
||||
// Layout::UnionNullableUnwrappedFalse(_) => todo!(),
|
||||
// Layout::RecursivePointer => todo!(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Invariant: the layouts are sorted from biggest to smallest alignment
|
||||
fn align_of_layout_slice(&self, slice: Slice<Layout>) -> u16 {
|
||||
match slice.into_iter().next() {
|
||||
None => 0,
|
||||
Some(first_index) => self.align_of_layout_index(first_index),
|
||||
}
|
||||
}
|
||||
|
||||
fn align_of_layout_slices(&self, slice: Slice<Slice<Layout>>) -> u16 {
|
||||
slice
|
||||
.into_iter()
|
||||
.map(|index| self.layout_slices[index.index as usize])
|
||||
.map(|slice| self.align_of_layout_slice(slice))
|
||||
.max()
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
/// Invariant: the layouts are sorted from biggest to smallest alignment
|
||||
fn size_of_layout_slice(&self, slice: Slice<Layout>) -> u16 {
|
||||
match slice.into_iter().next() {
|
||||
None => 0,
|
||||
Some(first_index) => {
|
||||
let alignment = self.align_of_layout_index(first_index);
|
||||
|
||||
let mut sum = 0;
|
||||
|
||||
for index in slice.into_iter() {
|
||||
sum += self.size_of_layout_index(index);
|
||||
}
|
||||
|
||||
round_up_to_alignment(sum, alignment)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size_of_layout_index(&self, index: Index<Layout>) -> u16 {
|
||||
let layout = self.layouts[index.index as usize];
|
||||
|
||||
self.size_of_layout(layout)
|
||||
}
|
||||
|
||||
pub fn size_of_layout(&self, layout: Layout) -> u16 {
|
||||
let usize_int_width = match self.target_info.ptr_width() {
|
||||
roc_target::PtrWidth::Bytes4 => IntWidth::U32,
|
||||
roc_target::PtrWidth::Bytes8 => IntWidth::U64,
|
||||
};
|
||||
|
||||
let ptr_width = usize_int_width.stack_size() as u16;
|
||||
|
||||
match layout {
|
||||
Layout::Reserved => unreachable!(),
|
||||
Layout::Int(int_width) => int_width.stack_size() as _,
|
||||
Layout::Float(float_width) => float_width as _,
|
||||
Layout::Decimal => (std::mem::size_of::<roc_std::RocDec>()) as _,
|
||||
Layout::Str | Layout::Dict(_) | Layout::Set(_) | Layout::List(_) => 2 * ptr_width,
|
||||
Layout::Struct(slice) => self.size_of_layout_slice(slice),
|
||||
Layout::Boxed(_) | Layout::UnionRecursive(_) => ptr_width,
|
||||
Layout::UnionNonRecursive(slices) if slices.is_empty() => 0,
|
||||
Layout::UnionNonRecursive(slices) => {
|
||||
let tag_id = IntWidth::I64;
|
||||
|
||||
let max_slice_size = slices
|
||||
.into_iter()
|
||||
.map(|index| self.layout_slices[index.index as usize])
|
||||
.map(|slice| self.align_of_layout_slice(slice))
|
||||
.max()
|
||||
.unwrap_or_default();
|
||||
|
||||
tag_id.stack_size() as u16 + max_slice_size
|
||||
}
|
||||
// Layout::UnionNonNullableUnwrapped(_) => todo!(),
|
||||
// Layout::UnionNullableWrapper { data, tag_id } => todo!(),
|
||||
// Layout::UnionNullableUnwrappedTrue(_) => todo!(),
|
||||
// Layout::UnionNullableUnwrappedFalse(_) => todo!(),
|
||||
// Layout::RecursivePointer => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum LayoutError {
|
||||
UnresolvedVariable(Variable),
|
||||
TypeError(()),
|
||||
}
|
||||
|
||||
impl Layout {
|
||||
pub const UNIT: Self = Self::Struct(Slice::new(0, 0));
|
||||
pub const VOID: Self = Self::UnionNonRecursive(Slice::new(0, 0));
|
||||
|
||||
pub const EMPTY_LIST: Self = Self::List(Layouts::VOID_INDEX);
|
||||
pub const EMPTY_DICT: Self = Self::Dict(Layouts::VOID_TUPLE);
|
||||
pub const EMPTY_SET: Self = Self::Set(Layouts::VOID_INDEX);
|
||||
|
||||
pub fn from_var(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
) -> Result<Layout, LayoutError> {
|
||||
// so we can set some things/clean up
|
||||
Self::from_var_help(layouts, subs, var)
|
||||
}
|
||||
|
||||
fn from_var_help(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
) -> Result<Layout, LayoutError> {
|
||||
let content = &subs.get_content_without_compacting(var);
|
||||
Self::from_content(layouts, subs, var, content)
|
||||
}
|
||||
|
||||
/// Used in situations where an unspecialized variable is not a problem,
|
||||
/// and we can substitute with `[]`, the empty tag union.
|
||||
/// e.g. an empty list literal has type `List *`. We can still generate code
|
||||
/// in those cases by just picking any concrete type for the list element,
|
||||
/// and we pick the empty tag union in practice.
|
||||
fn from_var_help_or_void(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
) -> Result<Layout, LayoutError> {
|
||||
let content = &subs.get_content_without_compacting(var);
|
||||
|
||||
match content {
|
||||
Content::FlexVar(_) | Content::RigidVar(_) => Ok(Layout::VOID),
|
||||
|
||||
_ => Self::from_content(layouts, subs, var, content),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_content(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
var: Variable,
|
||||
content: &Content,
|
||||
) -> Result<Layout, LayoutError> {
|
||||
use LayoutError::*;
|
||||
|
||||
match content {
|
||||
Content::FlexVar(_)
|
||||
| Content::RigidVar(_)
|
||||
| Content::FlexAbleVar(_, _)
|
||||
| Content::RigidAbleVar(_, _) => Err(UnresolvedVariable(var)),
|
||||
Content::RecursionVar {
|
||||
structure,
|
||||
opt_name: _,
|
||||
} => {
|
||||
let structure = subs.get_root_key_without_compacting(*structure);
|
||||
|
||||
let entry = layouts
|
||||
.recursion_variable_to_structure_variable_map
|
||||
.entry(structure);
|
||||
|
||||
match entry {
|
||||
Entry::Vacant(vacant) => {
|
||||
let reserved = Index::new(layouts.layouts.len() as _);
|
||||
layouts.layouts.push(Layout::Reserved);
|
||||
|
||||
vacant.insert(reserved);
|
||||
|
||||
let layout = Layout::from_var(layouts, subs, structure)?;
|
||||
|
||||
layouts.layouts[reserved.index as usize] = layout;
|
||||
|
||||
Ok(Layout::Boxed(reserved))
|
||||
}
|
||||
Entry::Occupied(occupied) => {
|
||||
let index = occupied.get();
|
||||
|
||||
Ok(Layout::Boxed(*index))
|
||||
}
|
||||
}
|
||||
}
|
||||
// Lambda set layout is same as tag union
|
||||
Content::LambdaSet(lset) => Self::from_lambda_set(layouts, subs, *lset),
|
||||
Content::Structure(flat_type) => Self::from_flat_type(layouts, subs, flat_type),
|
||||
Content::Alias(symbol, _, actual, _) => {
|
||||
let symbol = *symbol;
|
||||
|
||||
if let Some(int_width) = IntWidth::try_from_symbol(symbol) {
|
||||
return Ok(Layout::Int(int_width));
|
||||
}
|
||||
|
||||
if let Some(float_width) = FloatWidth::try_from_symbol(symbol) {
|
||||
return Ok(Layout::Float(float_width));
|
||||
}
|
||||
|
||||
match symbol {
|
||||
Symbol::NUM_DECIMAL => Ok(Layout::Decimal),
|
||||
|
||||
Symbol::NUM_NAT | Symbol::NUM_NATURAL => Ok(layouts.usize()),
|
||||
|
||||
_ => {
|
||||
// at this point we throw away alias information
|
||||
Self::from_var_help(layouts, subs, *actual)
|
||||
}
|
||||
}
|
||||
}
|
||||
Content::RangedNumber(_) => todo!(),
|
||||
Content::Error => Err(TypeError(())),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_lambda_set(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
lset: subs::LambdaSet,
|
||||
) -> Result<Layout, LayoutError> {
|
||||
let subs::LambdaSet {
|
||||
solved,
|
||||
recursion_var,
|
||||
unspecialized: _,
|
||||
ambient_function: _,
|
||||
} = lset;
|
||||
|
||||
// TODO: handle unspecialized lambda set
|
||||
|
||||
match recursion_var.into_variable() {
|
||||
Some(rec_var) => {
|
||||
let rec_var = subs.get_root_key_without_compacting(rec_var);
|
||||
|
||||
let cached = layouts
|
||||
.recursion_variable_to_structure_variable_map
|
||||
.get(&rec_var);
|
||||
|
||||
if let Some(layout_index) = cached {
|
||||
match layouts.layouts[layout_index.index as usize] {
|
||||
Layout::Reserved => {
|
||||
// we have to do the work here to fill this reserved variable in
|
||||
}
|
||||
other => {
|
||||
return Ok(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let slices = Self::from_slice_variable_slice(layouts, subs, solved.variables())?;
|
||||
|
||||
Ok(Layout::UnionRecursive(slices))
|
||||
}
|
||||
None => {
|
||||
let slices = Self::from_slice_variable_slice(layouts, subs, solved.variables())?;
|
||||
|
||||
Ok(Layout::UnionNonRecursive(slices))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn from_flat_type(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
flat_type: &FlatType,
|
||||
) -> Result<Layout, LayoutError> {
|
||||
match flat_type {
|
||||
FlatType::Apply(Symbol::LIST_LIST, arguments) => {
|
||||
debug_assert_eq!(arguments.len(), 1);
|
||||
|
||||
let element_var = subs.variables[arguments.start as usize];
|
||||
let element_layout = Self::from_var_help_or_void(layouts, subs, element_var)?;
|
||||
|
||||
let element_index = Index::new(layouts.layouts.len() as _);
|
||||
layouts.layouts.push(element_layout);
|
||||
|
||||
Ok(Layout::List(element_index))
|
||||
}
|
||||
|
||||
FlatType::Apply(Symbol::DICT_DICT, arguments) => {
|
||||
debug_assert_eq!(arguments.len(), 2);
|
||||
|
||||
let key_var = subs.variables[arguments.start as usize];
|
||||
let value_var = subs.variables[arguments.start as usize + 1];
|
||||
|
||||
let key_layout = Self::from_var_help_or_void(layouts, subs, key_var)?;
|
||||
let value_layout = Self::from_var_help_or_void(layouts, subs, value_var)?;
|
||||
|
||||
let index = Index::new(layouts.layouts.len() as _);
|
||||
layouts.layouts.push(key_layout);
|
||||
layouts.layouts.push(value_layout);
|
||||
|
||||
Ok(Layout::Dict(index))
|
||||
}
|
||||
|
||||
FlatType::Apply(Symbol::SET_SET, arguments) => {
|
||||
debug_assert_eq!(arguments.len(), 1);
|
||||
|
||||
let element_var = subs.variables[arguments.start as usize];
|
||||
let element_layout = Self::from_var_help_or_void(layouts, subs, element_var)?;
|
||||
|
||||
let element_index = Index::new(layouts.layouts.len() as _);
|
||||
layouts.layouts.push(element_layout);
|
||||
|
||||
Ok(Layout::Set(element_index))
|
||||
}
|
||||
|
||||
FlatType::Apply(symbol, _) => {
|
||||
unreachable!("Symbol {:?} does not have a layout", symbol)
|
||||
}
|
||||
|
||||
FlatType::Func(_arguments, lambda_set, _result) => {
|
||||
// in this case, a function (pointer) is represented by the environment it
|
||||
// captures: the lambda set
|
||||
|
||||
Self::from_var_help(layouts, subs, *lambda_set)
|
||||
}
|
||||
FlatType::Record(fields, ext) => {
|
||||
debug_assert!(ext_var_is_empty_record(subs, *ext));
|
||||
|
||||
let mut slice = Slice::reserve(layouts, fields.len());
|
||||
|
||||
let mut non_optional_fields = 0;
|
||||
let it = slice.indices().zip(fields.iter_all());
|
||||
for (target_index, (_, field_index, var_index)) in it {
|
||||
match subs.record_fields[field_index.index as usize] {
|
||||
RecordField::Optional(_) | RecordField::RigidOptional(_) => {
|
||||
// do nothing
|
||||
}
|
||||
RecordField::Required(_)
|
||||
| RecordField::Demanded(_)
|
||||
| RecordField::RigidRequired(_) => {
|
||||
let var = subs.variables[var_index.index as usize];
|
||||
let layout = Layout::from_var_help(layouts, subs, var)?;
|
||||
|
||||
layouts.layouts[target_index] = layout;
|
||||
|
||||
non_optional_fields += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we have some wasted space in the case of optional fields; so be it
|
||||
slice.length = non_optional_fields;
|
||||
|
||||
layouts.sort_slice_by_alignment(slice);
|
||||
|
||||
Ok(Layout::Struct(slice))
|
||||
}
|
||||
FlatType::Tuple(_elems, _ext) => {
|
||||
todo!();
|
||||
}
|
||||
FlatType::TagUnion(union_tags, ext) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, *ext));
|
||||
|
||||
let slices =
|
||||
Self::from_slice_variable_slice(layouts, subs, union_tags.variables())?;
|
||||
|
||||
Ok(Layout::UnionNonRecursive(slices))
|
||||
}
|
||||
|
||||
FlatType::FunctionOrTagUnion(_, _, ext) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, *ext));
|
||||
|
||||
// at this point we know this is a tag
|
||||
Ok(Layout::UNIT)
|
||||
}
|
||||
FlatType::RecursiveTagUnion(rec_var, union_tags, ext) => {
|
||||
debug_assert!(ext_var_is_empty_tag_union(subs, *ext));
|
||||
|
||||
let rec_var = subs.get_root_key_without_compacting(*rec_var);
|
||||
|
||||
let cached = layouts
|
||||
.recursion_variable_to_structure_variable_map
|
||||
.get(&rec_var);
|
||||
|
||||
if let Some(layout_index) = cached {
|
||||
match layouts.layouts[layout_index.index as usize] {
|
||||
Layout::Reserved => {
|
||||
// we have to do the work here to fill this reserved variable in
|
||||
}
|
||||
other => {
|
||||
return Ok(other);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let slices =
|
||||
Self::from_slice_variable_slice(layouts, subs, union_tags.variables())?;
|
||||
|
||||
Ok(Layout::UnionRecursive(slices))
|
||||
}
|
||||
FlatType::EmptyRecord | FlatType::EmptyTuple => Ok(Layout::UNIT),
|
||||
FlatType::EmptyTagUnion => Ok(Layout::VOID),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_slice_variable_slice(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
slice_variable_slice: roc_types::subs::SubsSlice<roc_types::subs::VariableSubsSlice>,
|
||||
) -> Result<Slice<Slice<Layout>>, LayoutError> {
|
||||
let slice = Slice::reserve(layouts, slice_variable_slice.len());
|
||||
|
||||
let variable_slices = &subs.variable_slices[slice_variable_slice.indices()];
|
||||
let it = slice.indices().zip(variable_slices);
|
||||
for (target_index, variable_slice) in it {
|
||||
let layout_slice = Layout::from_variable_slice(layouts, subs, *variable_slice)?;
|
||||
layouts.layout_slices[target_index] = layout_slice;
|
||||
}
|
||||
|
||||
Ok(slice)
|
||||
}
|
||||
|
||||
fn from_variable_slice(
|
||||
layouts: &mut Layouts,
|
||||
subs: &Subs,
|
||||
variable_subs_slice: roc_types::subs::VariableSubsSlice,
|
||||
) -> Result<Slice<Layout>, LayoutError> {
|
||||
let slice = Slice::reserve(layouts, variable_subs_slice.len());
|
||||
|
||||
let variable_slice = &subs.variables[variable_subs_slice.indices()];
|
||||
let it = slice.indices().zip(variable_slice);
|
||||
for (target_index, var) in it {
|
||||
let layout = Layout::from_var_help(layouts, subs, *var)?;
|
||||
layouts.layouts[target_index] = layout;
|
||||
}
|
||||
|
||||
layouts.sort_slice_by_alignment(slice);
|
||||
|
||||
Ok(slice)
|
||||
}
|
||||
}
|
|
@ -15,7 +15,6 @@ pub mod drop_specialization;
|
|||
pub mod inc_dec;
|
||||
pub mod ir;
|
||||
pub mod layout;
|
||||
pub mod layout_soa;
|
||||
pub mod low_level;
|
||||
pub mod reset_reuse;
|
||||
pub mod tail_recursion;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue