mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 06:14:46 +00:00
fix zero-sized closures
This commit is contained in:
parent
2267a1174a
commit
c20b40a7d2
6 changed files with 56 additions and 35 deletions
|
@ -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,
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue