fix zero-sized closures

This commit is contained in:
Folkert 2020-11-08 03:04:28 +01:00
parent 2267a1174a
commit c20b40a7d2
6 changed files with 56 additions and 35 deletions

View file

@ -905,7 +905,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
Struct(sorted_fields) => { Struct(sorted_fields) => {
let ctx = env.context; let ctx = env.context;
let builder = env.builder; let builder = env.builder;
let ptr_bytes = env.ptr_bytes;
// Determine types // Determine types
let num_fields = sorted_fields.len(); let num_fields = sorted_fields.len();
@ -916,7 +915,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
// Zero-sized fields have no runtime representation. // Zero-sized fields have no runtime representation.
// The layout of the struct expects them to be dropped! // The layout of the struct expects them to be dropped!
let (field_expr, field_layout) = load_symbol_and_layout(env, scope, symbol); let (field_expr, field_layout) = load_symbol_and_layout(env, scope, symbol);
if field_layout.stack_size(ptr_bytes) != 0 { if !field_layout.is_dropped_because_empty() {
field_types.push(basic_type_from_layout( field_types.push(basic_type_from_layout(
env.arena, env.arena,
env.context, env.context,
@ -956,7 +955,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
let it = arguments.iter(); let it = arguments.iter();
let ctx = env.context; let ctx = env.context;
let ptr_bytes = env.ptr_bytes;
let builder = env.builder; let builder = env.builder;
// Determine types // Determine types
@ -966,9 +964,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
for field_symbol in it { for field_symbol in it {
let (val, field_layout) = load_symbol_and_layout(env, scope, field_symbol); let (val, field_layout) = load_symbol_and_layout(env, scope, field_symbol);
// Zero-sized fields have no runtime representation. if !field_layout.is_dropped_because_empty() {
// The layout of the struct expects them to be dropped!
if field_layout.stack_size(ptr_bytes) != 0 {
let field_type = basic_type_from_layout( let field_type = basic_type_from_layout(
env.arena, env.arena,
env.context, env.context,

View file

@ -1535,16 +1535,26 @@ fn specialize_external<'a>(
}; };
for (index, (symbol, variable)) in captured.iter().enumerate() { for (index, (symbol, variable)) in captured.iter().enumerate() {
let expr = Expr::AccessAtIndex {
index: index as _,
field_layouts,
structure: Symbol::ARG_CLOSURE,
wrapped,
};
// layout is cached anyway, re-using the one found above leads to // layout is cached anyway, re-using the one found above leads to
// issues (combining by-ref and by-move in pattern match // issues (combining by-ref and by-move in pattern match
let layout = layout_cache.from_var(env.arena, *variable, env.subs)?; let layout = layout_cache.from_var(env.arena, *variable, env.subs)?;
// 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
// data struct
let expr = {
if layout.is_dropped_because_empty() {
Expr::Struct(&[])
} else {
Expr::AccessAtIndex {
index: index as _,
field_layouts,
structure: Symbol::ARG_CLOSURE,
wrapped,
}
}
};
specialized_body = Stmt::Let(*symbol, expr, layout, env.arena.alloc(specialized_body)); specialized_body = Stmt::Let(*symbol, expr, layout, env.arena.alloc(specialized_body));
} }
} }
@ -2946,14 +2956,22 @@ pub fn with_hole<'a>(
let symbols = let symbols =
Vec::from_iter_in(captured_symbols.iter().map(|x| x.0), env.arena) Vec::from_iter_in(captured_symbols.iter().map(|x| x.0), env.arena)
.into_bump_slice(); .into_bump_slice();
let expr = Expr::Struct(symbols);
stmt = Stmt::Let( // define the closure data, unless it's a basic unwrapped type already
closure_data, match closure_layout.build_closure_data(name, symbols) {
expr, Ok(expr) => {
closure_data_layout.clone(), stmt = Stmt::Let(
env.arena.alloc(stmt), 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(name, function_ptr_layout.clone()); let expr = Expr::FunctionPointer(name, function_ptr_layout.clone());

View file

@ -45,9 +45,8 @@ pub struct ClosureLayout<'a> {
} }
impl<'a> ClosureLayout<'a> { impl<'a> ClosureLayout<'a> {
#[allow(dead_code)]
fn from_unit(arena: &'a Bump) -> Self { fn from_unit(arena: &'a Bump) -> Self {
let layout = Layout::Struct(&[]); let layout = Layout::PhantomEmptyStruct;
let layouts = arena.alloc(layout); let layouts = arena.alloc(layout);
ClosureLayout { ClosureLayout {
captured: &[], captured: &[],
@ -127,10 +126,8 @@ impl<'a> ClosureLayout<'a> {
UnitWithArguments => { UnitWithArguments => {
// the closure layout is zero-sized, but there is something in it (e.g. `{}`) // the closure layout is zero-sized, but there is something in it (e.g. `{}`)
// TODO figure out what to do in this case. let closure_layout = ClosureLayout::from_unit(arena);
// let closure_layout = ClosureLayout::from_unit(arena); Ok(Some(closure_layout))
// Ok(Some(closure_layout))
Ok(None)
} }
BoolUnion { .. } => { BoolUnion { .. } => {
let closure_layout = ClosureLayout::from_bool(arena); let closure_layout = ClosureLayout::from_bool(arena);
@ -245,6 +242,12 @@ impl<'a> ClosureLayout<'a> {
Ok(expr) Ok(expr)
} }
Layout::PhantomEmptyStruct => {
debug_assert_eq!(symbols.len(), 1);
Ok(Expr::Struct(&[]))
}
_ => { _ => {
debug_assert_eq!(symbols.len(), 1); debug_assert_eq!(symbols.len(), 1);
@ -372,11 +375,15 @@ impl<'a> Layout<'a> {
} }
} }
pub fn is_zero_sized(&self) -> bool { pub fn is_dropped_because_empty(&self) -> bool {
// For this calculation, we don't need an accurate // For this calculation, we don't need an accurate
// stack size, we just need to know whether it's zero, // stack size, we just need to know whether it's zero,
// so it's fine to use a pointer size of 1. // so it's fine to use a pointer size of 1.
self.stack_size(1) == 0 if let Layout::PhantomEmptyStruct = self {
false
} else {
self.stack_size(1) == 0
}
} }
pub fn stack_size(&self, pointer_size: u32) -> u32 { pub fn stack_size(&self, pointer_size: u32) -> u32 {
@ -765,7 +772,7 @@ fn layout_from_flat_type<'a>(
match Layout::from_var(env, field_var) { match Layout::from_var(env, field_var) {
Ok(layout) => { Ok(layout) => {
// Drop any zero-sized fields like {}. // Drop any zero-sized fields like {}.
if !layout.is_zero_sized() { if !layout.is_dropped_because_empty() {
layouts.push(layout); layouts.push(layout);
} }
} }
@ -874,7 +881,7 @@ pub fn sort_record_fields<'a>(
let layout = Layout::from_var(&mut env, var).expect("invalid layout from var"); let layout = Layout::from_var(&mut env, var).expect("invalid layout from var");
// Drop any zero-sized fields like {} // Drop any zero-sized fields like {}
if !layout.is_zero_sized() { if !layout.is_dropped_because_empty() {
sorted_fields.push((label, var, Ok(layout))); sorted_fields.push((label, var, Ok(layout)));
} }
} }
@ -970,7 +977,7 @@ fn union_sorted_tags_help<'a>(
match Layout::from_var(&mut env, var) { match Layout::from_var(&mut env, var) {
Ok(layout) => { Ok(layout) => {
// Drop any zero-sized arguments like {} // Drop any zero-sized arguments like {}
if !layout.is_zero_sized() { if !layout.is_dropped_because_empty() {
layouts.push(layout); layouts.push(layout);
} else { } else {
contains_zero_sized = true; contains_zero_sized = true;
@ -1015,7 +1022,7 @@ fn union_sorted_tags_help<'a>(
match Layout::from_var(&mut env, var) { match Layout::from_var(&mut env, var) {
Ok(layout) => { Ok(layout) => {
// Drop any zero-sized arguments like {} // Drop any zero-sized arguments like {}
if !layout.is_zero_sized() { if !layout.is_dropped_because_empty() {
has_any_arguments = true; has_any_arguments = true;
arg_layouts.push(layout); arg_layouts.push(layout);

View file

@ -3,6 +3,6 @@ app Main provides [ main ] imports [ Effect ]
main : Effect.Effect {} as Fx main : Effect.Effect {} as Fx
main = main =
Effect.putLine "Write a thing!" Effect.putLine "Write a thing!"
|> Effect.after (\{} -> Effect.getLine) |> Effect.after (\{} -> Effect.getLine {})
|> Effect.after (\line -> Effect.putLine line) |> Effect.after (\line -> Effect.putLine line)

View file

@ -6,5 +6,5 @@ platform folkertdev/foo
{ {
putChar : Int -> Effect {}, putChar : Int -> Effect {},
putLine : Str -> Effect {}, putLine : Str -> Effect {},
getLine : Effect Str getLine : {} -> Effect Str
} }

View file

@ -38,7 +38,7 @@ pub fn roc_fx_putLine(line: RocStr) -> () {
} }
#[no_mangle] #[no_mangle]
pub fn roc_fx_getLine() -> RocStr { pub fn roc_fx_getLine(_: ()) -> RocStr {
use std::io::{self, BufRead}; use std::io::{self, BufRead};
let stdin = io::stdin(); let stdin = io::stdin();