mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-11 05:34:11 +03:00
make getLine : Effect {}
work
This commit is contained in:
parent
1bce119b0a
commit
0d5aaf2aaa
@ -1064,4 +1064,52 @@ mod gen_primitives {
|
||||
f64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_wrapped_function_pointer() {
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
|
||||
foo : Effect {}
|
||||
foo = @Effect \{} -> {}
|
||||
|
||||
main : Effect {}
|
||||
main = foo
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i64,
|
||||
|_| 1
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn return_wrapped_closure() {
|
||||
assert_non_opt_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
app Test provides [ main ] imports []
|
||||
|
||||
Effect a : [ @Effect ({} -> a) ]
|
||||
|
||||
foo : Effect {}
|
||||
foo =
|
||||
x = 5
|
||||
|
||||
@Effect \{} -> if x > 3 then {} else {}
|
||||
|
||||
main : Effect {}
|
||||
main = foo
|
||||
"#
|
||||
),
|
||||
1,
|
||||
i64,
|
||||
|_| 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1613,7 +1613,7 @@ fn update<'a>(
|
||||
// state.timings.insert(module_id, module_timing);
|
||||
|
||||
// display the mono IR of the module, for debug purposes
|
||||
if false {
|
||||
if roc_mono::ir::PRETTY_PRINT_IR_SYMBOLS {
|
||||
let procs_string = state
|
||||
.procedures
|
||||
.values()
|
||||
|
@ -14,6 +14,8 @@ use roc_types::subs::{Content, FlatType, Subs, Variable};
|
||||
use std::collections::HashMap;
|
||||
use ven_pretty::{BoxAllocator, DocAllocator, DocBuilder};
|
||||
|
||||
pub const PRETTY_PRINT_IR_SYMBOLS: bool = false;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum MonoProblem {
|
||||
PatternProblem(crate::exhaustive::Error),
|
||||
@ -902,8 +904,11 @@ where
|
||||
D::Doc: Clone,
|
||||
A: Clone,
|
||||
{
|
||||
alloc.text(format!("{}", symbol))
|
||||
// alloc.text(format!("{:?}", symbol))
|
||||
if PRETTY_PRINT_IR_SYMBOLS {
|
||||
alloc.text(format!("{:?}", symbol))
|
||||
} else {
|
||||
alloc.text(format!("{}", symbol))
|
||||
}
|
||||
}
|
||||
|
||||
fn join_point_to_doc<'b, D, A>(alloc: &'b D, symbol: JoinPointId) -> DocBuilder<'b, D, A>
|
||||
@ -1514,51 +1519,9 @@ fn specialize_external<'a>(
|
||||
pattern_symbols
|
||||
};
|
||||
|
||||
let (proc_args, opt_closure_layout, ret_layout) =
|
||||
let specialized =
|
||||
build_specialized_proc_from_var(env, layout_cache, proc_name, pattern_symbols, fn_var)?;
|
||||
|
||||
let mut specialized_body = from_can(env, fn_var, body, procs, layout_cache);
|
||||
// unpack the closure symbols, if any
|
||||
if let CapturedSymbols::Captured(captured) = captured_symbols {
|
||||
let mut layouts = Vec::with_capacity_in(captured.len(), env.arena);
|
||||
|
||||
for (_, variable) in captured.iter() {
|
||||
let layout = layout_cache.from_var(env.arena, *variable, env.subs)?;
|
||||
layouts.push(layout);
|
||||
}
|
||||
|
||||
let field_layouts = layouts.into_bump_slice();
|
||||
|
||||
let wrapped = match &opt_closure_layout {
|
||||
Some(x) => x.get_wrapped(),
|
||||
None => unreachable!("symbols are captured, so this must be a closure"),
|
||||
};
|
||||
|
||||
for (index, (symbol, variable)) in captured.iter().enumerate() {
|
||||
// layout is cached anyway, re-using the one found above leads to
|
||||
// issues (combining by-ref and by-move in pattern match
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// determine the layout of aliases/rigids exposed to the host
|
||||
let host_exposed_layouts = if host_exposed_variables.is_empty() {
|
||||
HostExposedLayouts::NotHostExposed
|
||||
@ -1578,39 +1541,141 @@ fn specialize_external<'a>(
|
||||
}
|
||||
};
|
||||
|
||||
// reset subs, so we don't get type errors when specializing for a different signature
|
||||
layout_cache.rollback_to(cache_snapshot);
|
||||
env.subs.rollback_to(snapshot);
|
||||
|
||||
let recursivity = if is_self_recursive {
|
||||
SelfRecursive::SelfRecursive(JoinPointId(env.unique_symbol()))
|
||||
} else {
|
||||
SelfRecursive::NotSelfRecursive
|
||||
};
|
||||
|
||||
let closure_data_layout = match opt_closure_layout {
|
||||
Some(closure_layout) => Some(closure_layout.as_named_layout(proc_name)),
|
||||
None => None,
|
||||
};
|
||||
let mut specialized_body = from_can(env, fn_var, body, procs, layout_cache);
|
||||
|
||||
let proc = Proc {
|
||||
name: proc_name,
|
||||
args: proc_args,
|
||||
body: specialized_body,
|
||||
closure_data_layout,
|
||||
ret_layout,
|
||||
is_self_recursive: recursivity,
|
||||
host_exposed_layouts,
|
||||
};
|
||||
match specialized {
|
||||
SpecializedLayout::FunctionPointerBody {
|
||||
arguments,
|
||||
ret_layout,
|
||||
closure: opt_closure_layout,
|
||||
} => {
|
||||
// this is a function body like
|
||||
//
|
||||
// foo = Num.add
|
||||
//
|
||||
// we need to expand this to
|
||||
//
|
||||
// foo = \x,y -> Num.add x y
|
||||
|
||||
Ok(proc)
|
||||
// reset subs, so we don't get type errors when specializing for a different signature
|
||||
layout_cache.rollback_to(cache_snapshot);
|
||||
env.subs.rollback_to(snapshot);
|
||||
|
||||
let closure_data_layout = match opt_closure_layout {
|
||||
Some(closure_layout) => Some(closure_layout.as_named_layout(proc_name)),
|
||||
None => None,
|
||||
};
|
||||
|
||||
// I'm not sure how to handle the closure case, does it ever occur?
|
||||
debug_assert_eq!(closure_data_layout, None);
|
||||
debug_assert!(matches!(captured_symbols, CapturedSymbols::None));
|
||||
|
||||
// this will be a thunk returning a function, so its ret_layout must be a function!
|
||||
let full_layout = Layout::FunctionPointer(arguments, env.arena.alloc(ret_layout));
|
||||
|
||||
let proc = Proc {
|
||||
name: proc_name,
|
||||
args: &[],
|
||||
body: specialized_body,
|
||||
closure_data_layout,
|
||||
ret_layout: full_layout,
|
||||
is_self_recursive: recursivity,
|
||||
host_exposed_layouts,
|
||||
};
|
||||
|
||||
Ok(proc)
|
||||
}
|
||||
SpecializedLayout::FunctionBody {
|
||||
arguments: proc_args,
|
||||
closure: opt_closure_layout,
|
||||
ret_layout,
|
||||
} => {
|
||||
// unpack the closure symbols, if any
|
||||
if let CapturedSymbols::Captured(captured) = captured_symbols {
|
||||
let mut layouts = Vec::with_capacity_in(captured.len(), env.arena);
|
||||
|
||||
for (_, variable) in captured.iter() {
|
||||
let layout = layout_cache.from_var(env.arena, *variable, env.subs)?;
|
||||
layouts.push(layout);
|
||||
}
|
||||
|
||||
let field_layouts = layouts.into_bump_slice();
|
||||
|
||||
let wrapped = match &opt_closure_layout {
|
||||
Some(x) => x.get_wrapped(),
|
||||
None => unreachable!("symbols are captured, so this must be a closure"),
|
||||
};
|
||||
|
||||
for (index, (symbol, variable)) in captured.iter().enumerate() {
|
||||
// layout is cached anyway, re-using the one found above leads to
|
||||
// issues (combining by-ref and by-move in pattern match
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// reset subs, so we don't get type errors when specializing for a different signature
|
||||
layout_cache.rollback_to(cache_snapshot);
|
||||
env.subs.rollback_to(snapshot);
|
||||
|
||||
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,
|
||||
host_exposed_layouts,
|
||||
};
|
||||
|
||||
Ok(proc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type SpecializedLayout<'a> = (
|
||||
&'a [(Layout<'a>, Symbol)],
|
||||
Option<ClosureLayout<'a>>,
|
||||
Layout<'a>,
|
||||
);
|
||||
enum SpecializedLayout<'a> {
|
||||
/// A body like `foo = \a,b,c -> ...`
|
||||
FunctionBody {
|
||||
arguments: &'a [(Layout<'a>, Symbol)],
|
||||
closure: Option<ClosureLayout<'a>>,
|
||||
ret_layout: Layout<'a>,
|
||||
},
|
||||
/// A body like `foo = Num.add`
|
||||
FunctionPointerBody {
|
||||
arguments: &'a [Layout<'a>],
|
||||
closure: Option<ClosureLayout<'a>>,
|
||||
ret_layout: Layout<'a>,
|
||||
},
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
fn build_specialized_proc_from_var<'a>(
|
||||
@ -1739,6 +1804,7 @@ fn build_specialized_proc<'a>(
|
||||
let mut proc_args = Vec::with_capacity_in(pattern_layouts.len(), arena);
|
||||
|
||||
let pattern_layouts_len = pattern_layouts.len();
|
||||
let pattern_layouts_slice = pattern_layouts.clone().into_bump_slice();
|
||||
|
||||
for (arg_layout, arg_name) in pattern_layouts.into_iter().zip(pattern_symbols.iter()) {
|
||||
proc_args.push((arg_layout, *arg_name));
|
||||
@ -1761,6 +1827,7 @@ fn build_specialized_proc<'a>(
|
||||
// f_closure = { ptr: f, closure: x }
|
||||
//
|
||||
// then
|
||||
use SpecializedLayout::*;
|
||||
match opt_closure_layout {
|
||||
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`,
|
||||
@ -1776,7 +1843,11 @@ fn build_specialized_proc<'a>(
|
||||
|
||||
let proc_args = proc_args.into_bump_slice();
|
||||
|
||||
Ok((proc_args, Some(layout), ret_layout))
|
||||
Ok(FunctionBody {
|
||||
arguments: proc_args,
|
||||
closure: Some(layout),
|
||||
ret_layout,
|
||||
})
|
||||
}
|
||||
Some(layout) => {
|
||||
// else if there is a closure layout, we're building the `f_closure` value
|
||||
@ -1791,7 +1862,11 @@ fn build_specialized_proc<'a>(
|
||||
let closure_layout =
|
||||
Layout::Struct(arena.alloc([function_ptr_layout, closure_data_layout]));
|
||||
|
||||
Ok((&[], None, closure_layout))
|
||||
Ok(FunctionBody {
|
||||
arguments: &[],
|
||||
closure: None,
|
||||
ret_layout: closure_layout,
|
||||
})
|
||||
}
|
||||
None => {
|
||||
// else we're making a normal function, no closure problems to worry about
|
||||
@ -1805,16 +1880,32 @@ fn build_specialized_proc<'a>(
|
||||
Ordering::Equal => {
|
||||
let proc_args = proc_args.into_bump_slice();
|
||||
|
||||
Ok((proc_args, None, ret_layout))
|
||||
Ok(FunctionBody {
|
||||
arguments: proc_args,
|
||||
closure: None,
|
||||
ret_layout,
|
||||
})
|
||||
}
|
||||
Ordering::Greater => {
|
||||
// so far, the problem when hitting this branch was always somewhere else
|
||||
// I think this branch should not be reachable in a bugfree compiler
|
||||
panic!("more arguments (according to the layout) than argument symbols")
|
||||
}
|
||||
Ordering::Less => {
|
||||
panic!("more argument symbols than arguments (according to the layout)")
|
||||
if pattern_symbols.is_empty() {
|
||||
Ok(FunctionPointerBody {
|
||||
arguments: pattern_layouts_slice,
|
||||
closure: None,
|
||||
ret_layout,
|
||||
})
|
||||
} else {
|
||||
// so far, the problem when hitting this branch was always somewhere else
|
||||
// I think this branch should not be reachable in a bugfree compiler
|
||||
panic!(
|
||||
"more arguments (according to the layout) than argument symbols for {:?}",
|
||||
proc_name
|
||||
)
|
||||
}
|
||||
}
|
||||
Ordering::Less => panic!(
|
||||
"more argument symbols than arguments (according to the layout) for {:?}",
|
||||
proc_name
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2213,8 +2304,22 @@ pub fn with_hole<'a>(
|
||||
} else if symbol.module_id() != env.home && symbol.module_id() != ModuleId::ATTR {
|
||||
match layout_cache.from_var(env.arena, variable, env.subs) {
|
||||
Err(e) => panic!("invalid layout {:?}", e),
|
||||
Ok(Layout::FunctionPointer(_, _)) => {
|
||||
Ok(layout @ Layout::FunctionPointer(_, _)) => {
|
||||
add_needed_external(procs, env, variable, symbol);
|
||||
|
||||
match hole {
|
||||
Stmt::Jump(_, _) => todo!("not sure what to do in this case yet"),
|
||||
_ => {
|
||||
let expr = Expr::FunctionPointer(symbol, layout.clone());
|
||||
let new_symbol = env.unique_symbol();
|
||||
return Stmt::Let(
|
||||
new_symbol,
|
||||
expr,
|
||||
layout,
|
||||
env.arena.alloc(Stmt::Ret(new_symbol)),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
// this is a 0-arity thunk
|
||||
@ -4811,14 +4916,12 @@ fn call_by_name<'a>(
|
||||
]),
|
||||
);
|
||||
|
||||
let result = Stmt::Let(
|
||||
Stmt::Let(
|
||||
assigned,
|
||||
call,
|
||||
closure_struct_layout,
|
||||
hole,
|
||||
);
|
||||
|
||||
result
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
let call = Expr::FunctionCall {
|
||||
@ -4829,14 +4932,12 @@ fn call_by_name<'a>(
|
||||
args: field_symbols,
|
||||
};
|
||||
|
||||
let result = Stmt::Let(
|
||||
Stmt::Let(
|
||||
assigned,
|
||||
call,
|
||||
function_layout.full,
|
||||
hole,
|
||||
);
|
||||
|
||||
result
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
@ -3,5 +3,5 @@ app Main provides [ main ] imports [ Effect ]
|
||||
main : Effect.Effect {} as Fx
|
||||
main =
|
||||
Effect.putLine "Write a thing!"
|
||||
|> Effect.after (\{} -> Effect.getLine {})
|
||||
|> Effect.after (\{} -> Effect.getLine)
|
||||
|> Effect.after (\line -> Effect.putLine line)
|
||||
|
@ -6,7 +6,7 @@ platform folkertdev/foo
|
||||
{
|
||||
putChar : Int -> Effect {},
|
||||
putLine : Str -> Effect {},
|
||||
getLine : {} -> Effect Str
|
||||
getLine : Effect Str
|
||||
}
|
||||
|
||||
mainForHost : Effect {} as Fx
|
||||
|
@ -38,7 +38,7 @@ pub fn roc_fx_putLine(line: RocStr) -> () {
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub fn roc_fx_getLine(_: ()) -> RocStr {
|
||||
pub fn roc_fx_getLine() -> RocStr {
|
||||
use std::io::{self, BufRead};
|
||||
|
||||
let stdin = io::stdin();
|
||||
|
Loading…
Reference in New Issue
Block a user