code gen for variably-sized closures

This commit is contained in:
Folkert 2020-10-21 13:01:27 +02:00
parent 1953f563f7
commit c8c1a0678a
6 changed files with 322 additions and 311 deletions

View File

@ -717,6 +717,7 @@ pub fn constrain_expr(
arguments,
loc_body: boxed,
captured_symbols,
name,
..
} => {
use roc_can::expr::Recursive;
@ -799,8 +800,7 @@ pub fn constrain_expr(
));
}
let tag_name_string = format!("Closure_{}", closure_var.index());
let tag_name = roc_module::ident::TagName::Global(tag_name_string.into());
let tag_name = roc_module::ident::TagName::Closure(*name);
let closure_type = Type::TagUnion(
vec![(tag_name, tag_arguments)],
Box::new(Type::Variable(closure_ext_var)),

View File

@ -1844,13 +1844,11 @@ pub fn build_proc_header<'a, 'ctx, 'env>(
let ret_type = basic_type_from_layout(arena, context, &proc.ret_layout, env.ptr_bytes);
let mut arg_basic_types = Vec::with_capacity_in(args.len(), arena);
let mut arg_symbols = Vec::new_in(arena);
for (layout, arg_symbol) in args.iter() {
for (layout, _) in args.iter() {
let arg_type = basic_type_from_layout(arena, env.context, &layout, env.ptr_bytes);
arg_basic_types.push(arg_type);
arg_symbols.push(arg_symbol);
}
let fn_type = get_fn_type(&ret_type, &arg_basic_types);
@ -1895,14 +1893,29 @@ pub fn build_proc<'a, 'ctx, 'env>(
for (arg_val, (layout, arg_symbol)) in fn_val.get_param_iter().zip(args) {
set_name(arg_val, arg_symbol.ident_string(&env.interns));
// the closure argument (if any) comes in as an opaque sequence of bytes.
// we need to cast that to the specific closure data layout that the body expects
let value = if let Symbol::ARG_CLOSURE = *arg_symbol {
// blindly trust that there is a layout available for the closure data
let layout = proc.closure_data_layout.clone().unwrap();
// cast the input into the type that the body expects
let closure_data_type =
basic_type_from_layout(env.arena, env.context, &layout, env.ptr_bytes);
cast_basic_basic(env.builder, arg_val, closure_data_type)
} else {
arg_val
};
let alloca = create_entry_block_alloca(
env,
fn_val,
arg_val.get_type(),
value.get_type(),
arg_symbol.ident_string(&env.interns),
);
builder.build_store(alloca, arg_val);
builder.build_store(alloca, value);
scope.insert(*arg_symbol, (layout.clone(), alloca));
}

View File

@ -77,7 +77,7 @@ pub fn decrement_refcount_layout<'a, 'ctx, 'env>(
parent,
layout_ids,
field_ptr,
&closure_layout.as_layout(),
&closure_layout.as_block_of_memory_layout(),
)
}
}

View File

@ -936,8 +936,8 @@ mod gen_primitives {
f
main =
f = foo {}
f {}
g = foo {}
g {}
"#
),
42,
@ -971,8 +971,9 @@ mod gen_primitives {
}
#[test]
#[ignore]
fn specialize_closure() {
use roc_std::RocList;
assert_evals_to!(
indoc!(
r#"
@ -990,11 +991,12 @@ mod gen_primitives {
main =
items = foo {}
List.len items
# List.len items
List.map items (\f -> f {})
"#
),
2,
i64
RocList::from_slice(&[41, 42]),
RocList<i64>
);
}

View File

@ -60,6 +60,7 @@ pub struct Proc<'a> {
pub name: Symbol,
pub args: &'a [(Layout<'a>, Symbol)],
pub body: Stmt<'a>,
pub closure_data_layout: Option<Layout<'a>>,
pub ret_layout: Layout<'a>,
pub is_self_recursive: SelfRecursive,
}
@ -542,7 +543,10 @@ impl<'a> Procs<'a> {
.insert((symbol, layout.clone()), InProgress);
match specialize(env, self, symbol, layout_cache, pending, partial_proc) {
Ok((proc, layout)) => {
Ok((proc, _ignore_layout)) => {
// the `layout` is a function pointer, while `_ignore_layout` can be a
// closure. We only specialize functions, storing this value with a closure
// layout will give trouble.
self.specialized.insert((symbol, layout), Done(proc));
}
Err(error) => {
@ -1444,7 +1448,7 @@ fn specialize_external<'a>(
}
}
let (proc_args, ret_layout) =
let (proc_args, opt_closure_layout, ret_layout) =
build_specialized_proc_from_var(env, layout_cache, proc_name, pattern_symbols, fn_var)?;
// reset subs, so we don't get type errors when specializing for a different signature
@ -1457,10 +1461,16 @@ fn specialize_external<'a>(
SelfRecursive::NotSelfRecursive
};
let closure_data_layout = match opt_closure_layout {
Some(closure_layout) => Some(closure_layout.as_named_layout(proc_name)),
None => None,
};
let proc = Proc {
name: proc_name,
args: proc_args,
body: specialized_body,
closure_data_layout,
ret_layout,
is_self_recursive: recursivity,
};
@ -1468,6 +1478,12 @@ fn specialize_external<'a>(
Ok(proc)
}
type SpecializedLayout<'a> = (
&'a [(Layout<'a>, Symbol)],
Option<ClosureLayout<'a>>,
Layout<'a>,
);
#[allow(clippy::type_complexity)]
fn build_specialized_proc_from_var<'a>(
env: &mut Env<'a, '_>,
@ -1475,7 +1491,7 @@ fn build_specialized_proc_from_var<'a>(
proc_name: Symbol,
pattern_symbols: &[Symbol],
fn_var: Variable,
) -> Result<(&'a [(Layout<'a>, Symbol)], Layout<'a>), LayoutProblem> {
) -> Result<SpecializedLayout<'a>, LayoutProblem> {
match layout_cache.from_var(env.arena, fn_var, env.subs) {
Ok(Layout::FunctionPointer(pattern_layouts, ret_layout)) => {
let mut pattern_layouts_vec = Vec::with_capacity_in(pattern_layouts.len(), env.arena);
@ -1560,7 +1576,7 @@ fn build_specialized_proc_adapter<'a>(
pattern_vars: &[Variable],
opt_closure_layout: Option<ClosureLayout<'a>>,
ret_var: Variable,
) -> Result<(&'a [(Layout<'a>, Symbol)], Layout<'a>), LayoutProblem> {
) -> Result<SpecializedLayout<'a>, LayoutProblem> {
let mut arg_layouts = Vec::with_capacity_in(pattern_vars.len(), &env.arena);
for arg_var in pattern_vars {
@ -1586,12 +1602,12 @@ fn build_specialized_proc_adapter<'a>(
#[allow(clippy::type_complexity)]
fn build_specialized_proc<'a>(
arena: &'a Bump,
proc_name: Symbol,
_proc_name: Symbol,
pattern_symbols: &[Symbol],
pattern_layouts: Vec<Layout<'a>>,
opt_closure_layout: Option<ClosureLayout<'a>>,
ret_layout: Layout<'a>,
) -> Result<(&'a [(Layout<'a>, Symbol)], Layout<'a>), LayoutProblem> {
) -> Result<SpecializedLayout<'a>, LayoutProblem> {
let mut proc_args = Vec::with_capacity_in(pattern_layouts.len(), arena);
let pattern_layouts_len = pattern_layouts.len();
@ -1621,13 +1637,17 @@ fn build_specialized_proc<'a>(
Some(layout) if pattern_symbols.last() == Some(&Symbol::ARG_CLOSURE) => {
// here we define the lifted (now top-level) f function. Its final argument is `Symbol::ARG_CLOSURE`,
// it stores the closure structure (just an integer in this case)
proc_args.push((layout.as_named_layout(proc_name), Symbol::ARG_CLOSURE));
proc_args.push((layout.as_block_of_memory_layout(), Symbol::ARG_CLOSURE));
debug_assert_eq!(
pattern_layouts_len + 1,
pattern_symbols.len(),
"Tried to zip two vecs with different lengths!"
);
let proc_args = proc_args.into_bump_slice();
Ok((proc_args, Some(layout), ret_layout))
}
Some(layout) => {
// else if there is a closure layout, we're building the `f_closure` value
@ -1642,7 +1662,7 @@ fn build_specialized_proc<'a>(
let closure_layout =
Layout::Struct(arena.alloc([function_ptr_layout, closure_data_layout]));
return Ok((&[], closure_layout));
return Ok((&[], None, closure_layout));
}
None => {
// else we're making a normal function, no closure problems to worry about
@ -1658,12 +1678,11 @@ fn build_specialized_proc<'a>(
pattern_symbols.len(),
"Tried to zip two vecs with different lengths!"
);
let proc_args = proc_args.into_bump_slice();
Ok((proc_args, None, ret_layout))
}
}
let proc_args = proc_args.into_bump_slice();
Ok((proc_args, ret_layout))
}
fn specialize<'a>(
@ -1983,8 +2002,25 @@ pub fn with_hole<'a>(
match hole {
Stmt::Jump(id, _) => Stmt::Jump(*id, env.arena.alloc([symbol])),
_ => {
// if you see this, there is variable aliasing going on
Stmt::Ret(symbol)
let result = Stmt::Ret(symbol);
let original = symbol;
// we don't have a more accurate variable available, which means the variable
// from the partial_proc will be used. So far that has given the correct
// result, but I'm not sure this will continue to be the case in more complex
// examples.
let opt_fn_var = None;
// if this is a function symbol, ensure that it's properly specialized!
reuse_function_symbol(
env,
procs,
layout_cache,
opt_fn_var,
symbol,
result,
original,
)
}
}
}
@ -2647,7 +2683,7 @@ pub fn with_hole<'a>(
)
.unwrap();
let closure_data_layout = closure_layout.as_layout();
let closure_data_layout = closure_layout.as_block_of_memory_layout();
// define the function pointer
let function_ptr_layout = {
let mut temp =
@ -2807,7 +2843,8 @@ pub fn with_hole<'a>(
let closure_symbol = function_symbol;
// layout of the closure record
let closure_record_layout = closure_fields.as_layout();
let closure_record_layout =
closure_fields.as_block_of_memory_layout();
let arg_symbols = {
let mut temp =
@ -3106,183 +3143,28 @@ pub fn from_can<'a>(
let function_layout =
layout_cache.from_var(env.arena, function_type, env.subs);
if let Ok(Layout::Closure(
argument_layouts,
closure_fields,
ret_layout,
)) = &function_layout
{
let mut captured_symbols =
Vec::from_iter_in(captured_symbols, env.arena);
captured_symbols.sort();
let captured_symbols = captured_symbols.into_bump_slice();
procs.insert_named(
env,
layout_cache,
*symbol,
function_type,
arguments,
loc_body,
CapturedSymbols::Captured(captured_symbols),
is_self_recursive,
return_type,
);
let closure_data_layout = closure_fields.as_layout();
// define the function pointer
let function_ptr_layout = {
let mut temp = Vec::from_iter_in(
argument_layouts.iter().cloned(),
env.arena,
);
temp.push(closure_data_layout.clone());
Layout::FunctionPointer(temp.into_bump_slice(), ret_layout)
let captured_symbols =
if let Ok(Layout::Closure(_, _, _)) = &function_layout {
let mut temp = Vec::from_iter_in(captured_symbols, env.arena);
temp.sort();
CapturedSymbols::Captured(temp.into_bump_slice())
} else {
CapturedSymbols::None
};
let full_layout = function_ptr_layout.clone();
procs.insert_named(
env,
layout_cache,
*symbol,
function_type,
arguments,
loc_body,
captured_symbols,
is_self_recursive,
return_type,
);
let fn_var = function_type;
let proc_name = *symbol;
let pending = PendingSpecialization::from_var(env.subs, fn_var);
// When requested (that is, when procs.pending_specializations is `Some`),
// store a pending specialization rather than specializing immediately.
//
// We do this so that we can do specialization in two passes: first,
// build the mono_expr with all the specialized calls in place (but
// no specializations performed yet), and then second, *after*
// de-duplicating requested specializations (since multiple modules
// which could be getting monomorphized in parallel might request
// the same specialization independently), we work through the
// queue of pending specializations to complete each specialization
// exactly once.
match &mut procs.pending_specializations {
Some(pending_specializations) => {
// register the pending specialization, so this gets code genned later
add_pending(
pending_specializations,
proc_name,
full_layout.clone(),
pending,
);
}
None => {
let opt_partial_proc = procs.partial_procs.get(&proc_name);
match opt_partial_proc {
None => panic!("invalid proc"),
Some(partial_proc) => {
// TODO should pending_procs hold a Rc<Proc> to avoid this .clone()?
let partial_proc = partial_proc.clone();
// Mark this proc as in-progress, so if we're dealing with
// mutually recursive functions, we don't loop forever.
// (We had a bug around this before this system existed!)
procs.specialized.insert(
(proc_name, full_layout.clone()),
InProgress,
);
match specialize(
env,
procs,
proc_name,
layout_cache,
pending,
partial_proc,
) {
Ok((proc, _layout)) => {
// TODO sometimes the full_layout is a
// function pointer, but the layout is a
// closure. Figure out how to handle this
// debug_assert_eq!(full_layout, layout);
// let function_layout = FunctionLayouts::from_layout(layout);
procs.specialized.remove(&(
proc_name,
full_layout.clone(),
));
procs.specialized.insert(
(
proc_name,
// function_layout.full.clone(),
full_layout.clone(),
),
Done(proc),
);
}
Err(error) => {
panic!("procedure is invalid {:?}", error);
}
}
}
}
}
}
let mut stmt = from_can(env, cont.value, procs, layout_cache);
let function_pointer = env.unique_symbol();
let closure_data = env.unique_symbol();
// define the closure
let expr =
Expr::Struct(env.arena.alloc([function_pointer, closure_data]));
stmt = Stmt::Let(
*symbol,
expr,
Layout::Struct(env.arena.alloc([
function_ptr_layout.clone(),
closure_data_layout.clone(),
])),
env.arena.alloc(stmt),
);
// define the closure data
let symbols = Vec::from_iter_in(
captured_symbols.iter().map(|x| x.0),
env.arena,
)
.into_bump_slice();
let expr = Expr::Struct(symbols);
stmt = Stmt::Let(
closure_data,
expr,
closure_data_layout.clone(),
env.arena.alloc(stmt),
);
let expr =
Expr::FunctionPointer(*symbol, function_ptr_layout.clone());
stmt = Stmt::Let(
function_pointer,
expr,
function_ptr_layout,
env.arena.alloc(stmt),
);
return stmt;
} else {
procs.insert_named(
env,
layout_cache,
*symbol,
function_type,
arguments,
loc_body,
CapturedSymbols::None,
is_self_recursive,
return_type,
);
return from_can(env, cont.value, procs, layout_cache);
}
return from_can(env, cont.value, procs, layout_cache);
}
_ => unreachable!(),
}
@ -4225,6 +4107,133 @@ fn possible_reuse_symbol<'a>(
}
}
/// If the symbol is a function, make sure it is properly specialized
fn reuse_function_symbol<'a>(
env: &mut Env<'a, '_>,
procs: &mut Procs<'a>,
layout_cache: &mut LayoutCache<'a>,
arg_var: Option<Variable>,
symbol: Symbol,
result: Stmt<'a>,
original: Symbol,
) -> Stmt<'a> {
match procs.partial_procs.get(&original) {
None => result,
Some(partial_proc) => {
let arg_var = arg_var.unwrap_or(partial_proc.annotation);
// this symbol is a function, that is used by-name (e.g. as an argument to another
// function). Register it with the current variable, then create a function pointer
// to it in the IR.
let layout = layout_cache
.from_var(env.arena, arg_var, env.subs)
.expect("creating layout does not fail");
// we have three kinds of functions really. Plain functions, closures by capture,
// and closures by unification. Here we record whether this function captures
// anything.
let captures = partial_proc.captured_symbols.captures();
let captured = partial_proc.captured_symbols.clone();
match layout {
Layout::Closure(argument_layouts, closure_layout, ret_layout) if captures => {
// this is a closure by capture, meaning it itself captures local variables.
// we've defined the closure as a (function_ptr, closure_data) pair already
let mut stmt = result;
let function_pointer = env.unique_symbol();
let closure_data = env.unique_symbol();
// let closure_data_layout = closure_layout.as_named_layout(original);
let closure_data_layout = closure_layout.as_block_of_memory_layout();
// define the function pointer
let function_ptr_layout = ClosureLayout::extend_function_layout(
env.arena,
argument_layouts,
closure_layout.clone(),
ret_layout,
);
procs.insert_passed_by_name(
env,
arg_var,
original,
function_ptr_layout.clone(),
layout_cache,
);
// define the closure
let expr = Expr::Struct(env.arena.alloc([function_pointer, closure_data]));
stmt = Stmt::Let(
symbol,
expr,
Layout::Struct(
env.arena
.alloc([function_ptr_layout.clone(), closure_data_layout.clone()]),
),
env.arena.alloc(stmt),
);
// define the closure data
let symbols = match captured {
CapturedSymbols::Captured(captured_symbols) => {
Vec::from_iter_in(captured_symbols.iter().map(|x| x.0), env.arena)
.into_bump_slice()
}
CapturedSymbols::None => unreachable!(),
};
// define the closure data, unless it's a basic unwrapped type already
match closure_layout.build_closure_data(original, symbols) {
Ok(expr) => {
stmt = Stmt::Let(
closure_data,
expr,
closure_data_layout.clone(),
env.arena.alloc(stmt),
);
}
Err(current) => {
// there is only one symbol captured, use that immediately
substitute_in_exprs(env.arena, &mut stmt, closure_data, current);
}
}
let expr = Expr::FunctionPointer(original, function_ptr_layout.clone());
stmt = Stmt::Let(
function_pointer,
expr,
function_ptr_layout,
env.arena.alloc(stmt),
);
stmt
}
_ => {
procs.insert_passed_by_name(
env,
arg_var,
original,
layout.clone(),
layout_cache,
);
Stmt::Let(
symbol,
Expr::FunctionPointer(original, layout.clone()),
layout,
env.arena.alloc(result),
)
}
}
}
}
}
fn assign_to_symbol<'a>(
env: &mut Env<'a, '_>,
procs: &mut Procs<'a>,
@ -4236,42 +4245,15 @@ fn assign_to_symbol<'a>(
) -> Stmt<'a> {
// if this argument is already a symbol, we don't need to re-define it
if let roc_can::expr::Expr::Var(original) = loc_arg.value {
match procs.partial_procs.get(&original) {
Some(partial_proc) => {
// this symbol is a function, that is used by-name (e.g. as an argument to another
// function). Register it with the current variable, then create a function pointer
// to it in the IR.
let layout = layout_cache
.from_var(env.arena, arg_var, env.subs)
.expect("creating layout does not fail");
// we have three kinds of functions really. Plain functions, closures by capture,
// and closures by unification. Here we record whether this function captures
// anything.
let captures = partial_proc.captured_symbols.captures();
drop(partial_proc);
procs.insert_passed_by_name(env, arg_var, original, layout.clone(), layout_cache);
match layout {
Layout::Closure(_, _, _) if captures => {
// this is a closure by capture, meaning it itself captures local variables.
// we've defined the closure as a (function_ptr, closure_data) pair already
// replace `symbol` with `original`
let mut stmt = result;
substitute_in_exprs(env.arena, &mut stmt, symbol, original);
stmt
}
_ => Stmt::Let(
symbol,
Expr::FunctionPointer(original, layout.clone()),
layout,
env.arena.alloc(result),
),
}
}
_ => result,
}
reuse_function_symbol(
env,
procs,
layout_cache,
Some(arg_var),
symbol,
result,
original,
)
} else {
with_hole(
env,

View File

@ -35,14 +35,13 @@ pub struct ClosureLayout<'a> {
/// the layout that this specific closure captures
captured: &'a [(TagName, &'a [Layout<'a>])],
/// the layout that represents the maximum size the closure layout can have
max_size: &'a [Layout<'a>],
max_size: &'a Layout<'a>,
}
impl<'a> ClosureLayout<'a> {
fn from_bool(arena: &'a Bump) -> Self {
let layout = Layout::Builtin(Builtin::Int1);
let layouts = arena.alloc([layout]);
let layouts = arena.alloc(layout);
ClosureLayout {
captured: &[],
max_size: layouts,
@ -50,47 +49,55 @@ impl<'a> ClosureLayout<'a> {
}
fn from_byte(arena: &'a Bump) -> Self {
let layout = Layout::Builtin(Builtin::Int8);
let layouts = arena.alloc([layout]);
let layouts = arena.alloc(layout);
ClosureLayout {
captured: &[],
max_size: layouts,
}
}
fn from_unwrapped(layouts: &'a [Layout<'a>]) -> Self {
fn from_unwrapped(arena: &'a Bump, layouts: &'a [Layout<'a>]) -> Self {
debug_assert!(!layouts.is_empty());
ClosureLayout {
captured: &[],
max_size: layouts,
if layouts.len() == 1 {
ClosureLayout {
captured: &[],
max_size: &layouts[0],
}
} else {
ClosureLayout {
captured: &[],
max_size: arena.alloc(Layout::Struct(layouts)),
}
}
}
fn from_wrapped(arena: &'a Bump, tags: &'a [(TagName, &'a [Layout<'a>])]) -> Self {
fn from_tag_union(arena: &'a Bump, tags: &'a [(TagName, &'a [Layout<'a>])]) -> Self {
debug_assert!(!tags.is_empty());
// NOTE we fabricate a pointer size here.
// That's fine because we don't care about the exact size, just the biggest one
let pointer_size = 8;
let mut largest_size = 0;
let mut max_size = &[] as &[_];
let mut tag_arguments = Vec::with_capacity_in(tags.len(), arena);
let mut foobar = Vec::with_capacity_in(tags.len(), arena);
for (name, tag_args_with_discr) in tags.iter() {
let tag_args = &tag_args_with_discr[1..];
let size = tag_args.iter().map(|l| l.stack_size(pointer_size)).sum();
// >= because some of our layouts have 0 size, but are still valid layouts
if size >= largest_size {
largest_size = size;
max_size = tag_args;
}
// drop the discriminant, essentially creating an untagged union
let tag_args = &tag_args_with_discr[0..];
tag_arguments.push((name.clone(), tag_args));
foobar.push(tag_args);
}
let arguments = tag_arguments.into_bump_slice();
ClosureLayout {
captured: tag_arguments.into_bump_slice(),
max_size,
captured: arguments,
max_size: arena.alloc(Layout::Union(foobar.into_bump_slice())),
}
}
pub fn get_wrapped(&self) -> crate::ir::Wrapped {
use crate::ir::Wrapped;
match self.max_size {
Layout::Struct(_) => Wrapped::RecordOrSingleTagUnion,
Layout::Union(_) => Wrapped::MultiTagUnion,
_ => Wrapped::SingleElementRecord,
}
}
@ -123,14 +130,14 @@ impl<'a> ClosureLayout<'a> {
}
Unwrapped(layouts) => {
let closure_layout =
ClosureLayout::from_unwrapped(layouts.into_bump_slice());
ClosureLayout::from_unwrapped(arena, layouts.into_bump_slice());
Ok(Some(closure_layout))
}
Wrapped(tags) => {
// Wrapped(Vec<'a, (TagName, &'a [Layout<'a>])>),
let closure_layout =
ClosureLayout::from_wrapped(arena, tags.into_bump_slice());
ClosureLayout::from_tag_union(arena, tags.into_bump_slice());
Ok(Some(closure_layout))
}
}
@ -150,7 +157,8 @@ impl<'a> ClosureLayout<'a> {
closure_layout: Self,
ret_layout: &'a Layout<'a>,
) -> Layout<'a> {
let closure_data_layout = closure_layout.as_layout();
let closure_data_layout = closure_layout.max_size;
// define the function pointer
let function_ptr_layout = {
let mut temp = Vec::from_iter_in(argument_layouts.iter().cloned(), arena);
@ -162,35 +170,28 @@ impl<'a> ClosureLayout<'a> {
}
pub fn stack_size(&self, pointer_size: u32) -> u32 {
self.max_size
.iter()
.map(|l| l.stack_size(pointer_size))
.sum()
self.max_size.stack_size(pointer_size)
}
pub fn contains_refcounted(&self) -> bool {
self.captured
.iter()
.map(|t| t.1)
.flatten()
.any(|l| l.contains_refcounted())
self.max_size.contains_refcounted()
}
pub fn safe_to_memcpy(&self) -> bool {
self.captured
.iter()
.map(|t| t.1)
.flatten()
.all(|l| l.safe_to_memcpy())
self.max_size.safe_to_memcpy()
}
pub fn as_named_layout(&self, symbol: Symbol) -> Layout<'a> {
let layouts = if self.captured.is_empty() {
self.max_size
self.max_size.clone()
} else if let Some((_, tag_args)) = self
.captured
.iter()
.find(|(tn, _)| *tn == TagName::Closure(symbol))
{
tag_args
if tag_args.len() == 1 {
tag_args[0].clone()
} else {
Layout::Struct(tag_args)
}
} else {
unreachable!(
"invariant broken, TagName::Closure({:?}) is not in {:?}",
@ -198,30 +199,38 @@ impl<'a> ClosureLayout<'a> {
);
};
if layouts.len() == 1 {
layouts[0].clone()
} else {
Layout::Struct(layouts)
}
}
pub fn as_layout(&self) -> Layout<'a> {
if self.captured.is_empty() {
if self.max_size.len() == 1 {
self.max_size[0].clone()
} else {
Layout::Struct(self.max_size)
}
} else {
panic!();
}
layouts
}
pub fn as_block_of_memory_layout(&self) -> Layout<'a> {
if self.max_size.len() == 1 {
self.max_size[0].clone()
} else {
Layout::Struct(self.max_size)
self.max_size.clone()
}
pub fn build_closure_data(
&self,
original: Symbol,
symbols: &'a [Symbol],
) -> Result<crate::ir::Expr<'a>, Symbol> {
use crate::ir::Expr;
match self.max_size {
Layout::Struct(fields) if fields.len() > 1 => Ok(Expr::Struct(symbols)),
Layout::Union(tags) => {
let expr = Expr::Tag {
tag_layout: Layout::Union(tags),
tag_name: TagName::Closure(original),
tag_id: 0 as u8,
union_size: tags.len() as u8,
arguments: symbols,
};
Ok(expr)
}
_ => {
debug_assert_eq!(symbols.len(), 1);
Err(symbols[0])
}
}
}
}
@ -503,13 +512,18 @@ impl<'a> LayoutCache<'a> {
let result = Layout::from_var(&mut env, var);
let cached_layout = match &result {
Ok(layout) => Cached(layout.clone()),
Err(problem) => Problem(problem.clone()),
};
// Don't actually cache. The layout cache is very hard to get right in the presence
// of specialization, it's turned of for now so an invalid cache is never the cause
// of a problem
if false {
let cached_layout = match &result {
Ok(layout) => Cached(layout.clone()),
Err(problem) => Problem(problem.clone()),
};
self.layouts
.update_value(cached_var, |existing| existing.value = cached_layout);
self.layouts
.update_value(cached_var, |existing| existing.value = cached_layout);
}
result
}