fix bug in closure argument unpacking

This commit is contained in:
Folkert 2020-12-10 20:45:19 +01:00
parent a9b3c74f2e
commit 84421ad06d
6 changed files with 104 additions and 59 deletions

View file

@ -1477,10 +1477,9 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
} }
Dec(symbol, cont) => { Dec(symbol, cont) => {
let (value, layout) = load_symbol_and_layout(env, scope, symbol); let (value, layout) = load_symbol_and_layout(env, scope, symbol);
let layout = layout.clone();
if layout.contains_refcounted() { if layout.contains_refcounted() {
decrement_refcount_layout(env, parent, layout_ids, value, &layout); decrement_refcount_layout(env, parent, layout_ids, value, layout);
} }
build_exp_stmt(env, layout_ids, scope, parent, cont) build_exp_stmt(env, layout_ids, scope, parent, cont)
@ -2323,33 +2322,14 @@ pub fn build_proc<'a, 'ctx, 'env>(
for (arg_val, (layout, arg_symbol)) in fn_val.get_param_iter().zip(args) { for (arg_val, (layout, arg_symbol)) in fn_val.get_param_iter().zip(args) {
set_name(arg_val, arg_symbol.ident_string(&env.interns)); 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 {
// generate a caller function (to be used by the host)
// build_closure_caller(env, fn_val);
// builder.position_at_end(entry);
// 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( let alloca = create_entry_block_alloca(
env, env,
fn_val, fn_val,
value.get_type(), arg_val.get_type(),
arg_symbol.ident_string(&env.interns), arg_symbol.ident_string(&env.interns),
); );
builder.build_store(alloca, value); builder.build_store(alloca, arg_val);
scope.insert(*arg_symbol, (layout.clone(), alloca)); scope.insert(*arg_symbol, (layout.clone(), alloca));
} }

View file

@ -1618,46 +1618,109 @@ fn specialize_external<'a>(
ret_layout, ret_layout,
} => { } => {
// unpack the closure symbols, if any // unpack the closure symbols, if any
if let CapturedSymbols::Captured(captured) = captured_symbols { match (opt_closure_layout.clone(), captured_symbols) {
let mut layouts = Vec::with_capacity_in(captured.len(), env.arena); (Some(closure_layout), CapturedSymbols::Captured(captured)) => {
debug_assert!(!captured.is_empty());
for (_, variable) in captured.iter() { let wrapped = closure_layout.get_wrapped();
let layout = layout_cache.from_var(env.arena, *variable, env.subs)?;
layouts.push(layout);
}
let field_layouts = layouts.into_bump_slice(); let internal_layout = closure_layout.internal_layout();
let wrapped = match &opt_closure_layout { match internal_layout {
Some(x) => x.get_wrapped(), Layout::Union(_) => {
None => unreachable!("symbols are captured, so this must be a closure"), // here we rely on the fact that a union in a closure would be stored in a one-element record
}; // a closure layout that is a union must be a of the shape `Closure1 ... | Closure2 ...`
let tag_layout = closure_layout.as_named_layout(proc_name);
for (index, (symbol, variable)) in captured.iter().enumerate() { match tag_layout {
// layout is cached anyway, re-using the one found above leads to Layout::Struct(field_layouts) => {
// issues (combining by-ref and by-move in pattern match // NOTE closure unions do not store the tag!
let layout = layout_cache.from_var(env.arena, *variable, env.subs)?; let field_layouts = &field_layouts[1..];
for (index, (symbol, _variable)) in captured.iter().enumerate()
// if the symbol has a layout that is dropped from data structures (e.g. `{}`) {
// then regenerate the symbol here. The value may not be present in the closure let expr = Expr::AccessAtIndex {
// data struct
let expr = {
if layout.is_dropped_because_empty() {
Expr::Struct(&[])
} else {
Expr::AccessAtIndex {
index: index as _, index: index as _,
field_layouts, field_layouts,
structure: Symbol::ARG_CLOSURE, structure: Symbol::ARG_CLOSURE,
wrapped, wrapped,
}
}
}; };
specialized_body = let layout = field_layouts[index].clone();
Stmt::Let(*symbol, expr, layout, env.arena.alloc(specialized_body));
specialized_body = Stmt::Let(
*symbol,
expr,
layout,
env.arena.alloc(specialized_body),
);
} }
} }
_ => unreachable!("closure tag must store a struct"),
}
}
Layout::Struct(field_layouts) => {
debug_assert_eq!(
captured.len(),
field_layouts.len(),
"{:?} captures {:?} but has layout {:?}",
proc_name,
&captured,
&field_layouts
);
if field_layouts.len() > 1 {
for (index, (symbol, _variable)) in captured.iter().enumerate() {
let expr = Expr::AccessAtIndex {
index: index as _,
field_layouts,
structure: Symbol::ARG_CLOSURE,
wrapped,
};
let layout = field_layouts[index].clone();
specialized_body = Stmt::Let(
*symbol,
expr,
layout,
env.arena.alloc(specialized_body),
);
}
} else {
let symbol = captured[0].0;
substitute_in_exprs(
env.arena,
&mut specialized_body,
symbol,
Symbol::ARG_CLOSURE,
);
}
}
_other => {
// the closure argument is not a union or a record
debug_assert_eq!(
captured.len(),
1,
"mismatch {:?} captures {:?}",
proc_name,
&captured
);
let symbol = captured[0].0;
substitute_in_exprs(
env.arena,
&mut specialized_body,
symbol,
Symbol::ARG_CLOSURE,
);
}
}
}
(None, CapturedSymbols::None) => {}
_ => unreachable!("to closure or not to closure?"),
}
// reset subs, so we don't get type errors when specializing for a different signature // reset subs, so we don't get type errors when specializing for a different signature
layout_cache.rollback_to(cache_snapshot); layout_cache.rollback_to(cache_snapshot);

View file

@ -227,6 +227,10 @@ impl<'a> ClosureLayout<'a> {
} }
} }
pub fn internal_layout(&self) -> Layout<'a> {
self.layout.clone()
}
pub fn build_closure_data( pub fn build_closure_data(
&self, &self,
original: Symbol, original: Symbol,

View file

@ -2278,8 +2278,7 @@ mod test_mono {
ret Test.3; ret Test.3;
procedure Test.3 (Test.11, #Attr.12): procedure Test.3 (Test.11, #Attr.12):
let Test.2 = Index 0 #Attr.12; ret #Attr.12;
ret Test.2;
procedure Test.0 (): procedure Test.0 ():
let Test.10 = Struct {}; let Test.10 = Struct {};
@ -2328,8 +2327,7 @@ mod test_mono {
ret Test.10; ret Test.10;
procedure Test.3 (Test.9, #Attr.12): procedure Test.3 (Test.9, #Attr.12):
let Test.2 = Index 0 #Attr.12; ret #Attr.12;
ret Test.2;
procedure Test.0 (): procedure Test.0 ():
let Test.8 = Struct {}; let Test.8 = Struct {};

View file

@ -3,7 +3,7 @@ app "effect-example"
imports [ base.Task.{ Task, after } ] imports [ base.Task.{ Task, after } ]
provides [ main ] to base provides [ main ] to base
main : Task.Task {} I64 main : Task.Task {} F64
main = main =
Task.after (Task.putLine "foo") \{} -> Task.after (Task.putLine "foo") \{} ->
Task.putLine "bar" Task.putLine "bar"

View file

@ -11,5 +11,5 @@ platform folkertdev/foo
getLine : Effect Str getLine : Effect Str
} }
mainForHost : Task.Task {} I64 as Fx mainForHost : Task.Task {} F64 as Fx
mainForHost = main mainForHost = main