mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
fix passing functions by name
This commit is contained in:
parent
c51345ac8b
commit
020b6154f9
6 changed files with 372 additions and 297 deletions
|
@ -46,7 +46,6 @@ pub enum OptLevel {
|
||||||
Optimize,
|
Optimize,
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub type Scope<'a, 'ctx> = ImMap<Symbol, (Layout<'a>, PointerValue<'ctx>)>;
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq)]
|
#[derive(Default, Debug, Clone, PartialEq)]
|
||||||
pub struct Scope<'a, 'ctx> {
|
pub struct Scope<'a, 'ctx> {
|
||||||
symbols: ImMap<Symbol, (Layout<'a>, PointerValue<'ctx>)>,
|
symbols: ImMap<Symbol, (Layout<'a>, PointerValue<'ctx>)>,
|
||||||
|
|
|
@ -100,10 +100,8 @@ mod gen_list {
|
||||||
empty : List Int
|
empty : List Int
|
||||||
empty =
|
empty =
|
||||||
[]
|
[]
|
||||||
|
|
||||||
|
|
||||||
List.map empty (\x -> x)
|
List.map empty (\x -> x)
|
||||||
|
|
||||||
main {}
|
main {}
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -117,10 +115,8 @@ mod gen_list {
|
||||||
nonEmpty : List Int
|
nonEmpty : List Int
|
||||||
nonEmpty =
|
nonEmpty =
|
||||||
[ 1 ]
|
[ 1 ]
|
||||||
|
|
||||||
|
|
||||||
List.map nonEmpty (\x -> x)
|
List.map nonEmpty (\x -> x)
|
||||||
|
|
||||||
main {}
|
main {}
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -134,10 +130,8 @@ mod gen_list {
|
||||||
nonEmpty : List Int
|
nonEmpty : List Int
|
||||||
nonEmpty =
|
nonEmpty =
|
||||||
[ 1 ]
|
[ 1 ]
|
||||||
|
|
||||||
|
|
||||||
List.map nonEmpty (\x -> x + 1)
|
List.map nonEmpty (\x -> x + 1)
|
||||||
|
|
||||||
main {}
|
main {}
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -152,9 +146,7 @@ mod gen_list {
|
||||||
nonEmpty : List Int
|
nonEmpty : List Int
|
||||||
nonEmpty =
|
nonEmpty =
|
||||||
[ 1, 2, 3, 4, 5 ]
|
[ 1, 2, 3, 4, 5 ]
|
||||||
|
|
||||||
List.map nonEmpty (\x -> x * 2)
|
List.map nonEmpty (\x -> x * 2)
|
||||||
|
|
||||||
main {}
|
main {}
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
@ -180,39 +172,39 @@ mod gen_list {
|
||||||
&'static [bool]
|
&'static [bool]
|
||||||
);
|
);
|
||||||
|
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// main = \{} ->
|
main = \{} ->
|
||||||
// nonEmpty : List Int
|
nonEmpty : List Int
|
||||||
// nonEmpty =
|
nonEmpty =
|
||||||
// [ 1, 1, -4, 1, 2 ]
|
[ 1, 1, -4, 1, 2 ]
|
||||||
//
|
|
||||||
// greaterThanOne : Int -> Bool
|
greaterThanOne : Int -> Bool
|
||||||
// greaterThanOne i =
|
greaterThanOne = \i ->
|
||||||
// i > 0
|
i > 0
|
||||||
//
|
|
||||||
// List.map nonEmpty greaterThanOne
|
List.map nonEmpty greaterThanOne
|
||||||
//
|
|
||||||
// main {}
|
main {}
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// &[true, true, false, true, true],
|
&[true, true, false, true, true],
|
||||||
// &'static [bool]
|
&'static [bool]
|
||||||
// );
|
);
|
||||||
|
|
||||||
// assert_evals_to!(
|
assert_evals_to!(
|
||||||
// indoc!(
|
indoc!(
|
||||||
// r#"
|
r#"
|
||||||
// main = \{} ->
|
main = \{} ->
|
||||||
// List.map [] (\x -> x > 0)
|
List.map [] (\x -> x > 0)
|
||||||
//
|
|
||||||
// main {}
|
main {}
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// &[],
|
&[],
|
||||||
// &'static [bool]
|
&'static [bool]
|
||||||
// );
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub fn infer_borrow<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||||
enum Key {
|
pub enum Key {
|
||||||
Declaration(Symbol),
|
Declaration(Symbol),
|
||||||
JoinPoint(JoinPointId),
|
JoinPoint(JoinPointId),
|
||||||
}
|
}
|
||||||
|
@ -66,6 +66,24 @@ pub struct ParamMap<'a> {
|
||||||
items: MutMap<Key, &'a [Param<'a>]>,
|
items: MutMap<Key, &'a [Param<'a>]>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for ParamMap<'a> {
|
||||||
|
type Item = (Key, &'a [Param<'a>]);
|
||||||
|
type IntoIter = <std::collections::HashMap<Key, &'a [Param<'a>]> as IntoIterator>::IntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.items.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> IntoIterator for &'a ParamMap<'a> {
|
||||||
|
type Item = (&'a Key, &'a &'a [Param<'a>]);
|
||||||
|
type IntoIter = <&'a std::collections::HashMap<Key, &'a [Param<'a>]> as IntoIterator>::IntoIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.items.iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a> ParamMap<'a> {
|
impl<'a> ParamMap<'a> {
|
||||||
pub fn get_symbol(&self, symbol: Symbol) -> Option<&'a [Param<'a>]> {
|
pub fn get_symbol(&self, symbol: Symbol) -> Option<&'a [Param<'a>]> {
|
||||||
let key = Key::Declaration(symbol);
|
let key = Key::Declaration(symbol);
|
||||||
|
|
|
@ -213,9 +213,24 @@ fn consume_expr(m: &VarMap, e: &Expr<'_>) -> bool {
|
||||||
|
|
||||||
impl<'a> Context<'a> {
|
impl<'a> Context<'a> {
|
||||||
pub fn new(arena: &'a Bump, param_map: &'a ParamMap<'a>) -> Self {
|
pub fn new(arena: &'a Bump, param_map: &'a ParamMap<'a>) -> Self {
|
||||||
|
let mut vars = MutMap::default();
|
||||||
|
|
||||||
|
for (key, _) in param_map.into_iter() {
|
||||||
|
if let crate::borrow::Key::Declaration(symbol) = key {
|
||||||
|
vars.insert(
|
||||||
|
*symbol,
|
||||||
|
VarInfo {
|
||||||
|
reference: false, // assume function symbols are global constants
|
||||||
|
persistent: true, // assume function symbols are global constants
|
||||||
|
consume: false, // no need to consume this variable
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
arena,
|
arena,
|
||||||
vars: MutMap::default(),
|
vars,
|
||||||
jp_live_vars: MutMap::default(),
|
jp_live_vars: MutMap::default(),
|
||||||
local_context: LocalContext::default(),
|
local_context: LocalContext::default(),
|
||||||
param_map,
|
param_map,
|
||||||
|
|
|
@ -362,6 +362,89 @@ impl<'a> Procs<'a> {
|
||||||
None => unreachable!("insert_exposed was called after the pending specializations phase had already completed!"),
|
None => unreachable!("insert_exposed was called after the pending specializations phase had already completed!"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// TODO
|
||||||
|
pub fn insert_passed_by_name(
|
||||||
|
&mut self,
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
fn_var: Variable,
|
||||||
|
name: Symbol,
|
||||||
|
layout: Layout<'a>,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
) {
|
||||||
|
let tuple = (name, layout);
|
||||||
|
|
||||||
|
// If we've already specialized this one, no further work is needed.
|
||||||
|
if self.specialized.contains_key(&tuple) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We're done with that tuple, so move layout back out to avoid cloning it.
|
||||||
|
let (name, layout) = tuple;
|
||||||
|
|
||||||
|
// now we have to pull some tricks to extract the return var and pattern vars from Subs
|
||||||
|
match get_args_ret_var(env.subs, fn_var) {
|
||||||
|
Some((pattern_vars, ret_var)) => {
|
||||||
|
let pattern_vars = Vec::from_iter_in(pattern_vars.into_iter(), env.arena);
|
||||||
|
let pending = PendingSpecialization {
|
||||||
|
pattern_vars,
|
||||||
|
ret_var,
|
||||||
|
fn_var,
|
||||||
|
};
|
||||||
|
|
||||||
|
// This should only be called when pending_specializations is Some.
|
||||||
|
// Otherwise, it's being called in the wrong pass!
|
||||||
|
match &mut self.pending_specializations {
|
||||||
|
Some(pending_specializations) => {
|
||||||
|
// register the pending specialization, so this gets code genned later
|
||||||
|
add_pending(pending_specializations, name, layout, pending)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let symbol = name;
|
||||||
|
|
||||||
|
// TODO should pending_procs hold a Rc<Proc>?
|
||||||
|
let partial_proc = self.partial_procs.get(&symbol).unwrap().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!)
|
||||||
|
self.specialized
|
||||||
|
.insert((symbol, layout.clone()), InProgress);
|
||||||
|
|
||||||
|
match specialize(env, self, symbol, layout_cache, pending, partial_proc) {
|
||||||
|
Ok(proc) => {
|
||||||
|
self.specialized
|
||||||
|
.insert((symbol, layout.clone()), Done(proc));
|
||||||
|
}
|
||||||
|
Err(error) => {
|
||||||
|
let error_msg =
|
||||||
|
format!("TODO generate a RuntimeError message for {:?}", error);
|
||||||
|
self.runtime_errors
|
||||||
|
.insert(symbol, env.arena.alloc(error_msg));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other => {
|
||||||
|
unreachable!(
|
||||||
|
"trying to insert a symbol that is not a function: {:?} {:?}",
|
||||||
|
name, other
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_args_ret_var(subs: &Subs, var: Variable) -> Option<(std::vec::Vec<Variable>, Variable)> {
|
||||||
|
match subs.get_without_compacting(var).content {
|
||||||
|
Content::Structure(FlatType::Func(pattern_vars, ret_var)) => Some((pattern_vars, ret_var)),
|
||||||
|
Content::Structure(FlatType::Apply(Symbol::ATTR_ATTR, args)) => {
|
||||||
|
get_args_ret_var(subs, args[1])
|
||||||
|
}
|
||||||
|
Content::Alias(_, _, actual) => get_args_ret_var(subs, actual),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn add_pending<'a>(
|
fn add_pending<'a>(
|
||||||
|
@ -621,7 +704,9 @@ impl<'a> Expr<'a> {
|
||||||
match self {
|
match self {
|
||||||
Literal(lit) => lit.to_doc(alloc),
|
Literal(lit) => lit.to_doc(alloc),
|
||||||
|
|
||||||
FunctionPointer(symbol, _) => symbol_to_doc(alloc, *symbol),
|
FunctionPointer(symbol, _) => alloc
|
||||||
|
.text("FunctionPointer ")
|
||||||
|
.append(symbol_to_doc(alloc, *symbol)),
|
||||||
|
|
||||||
FunctionCall {
|
FunctionCall {
|
||||||
call_type, args, ..
|
call_type, args, ..
|
||||||
|
@ -1364,12 +1449,9 @@ pub fn with_hole<'a>(
|
||||||
let mut field_symbols = Vec::with_capacity_in(field_layouts.len(), env.arena);
|
let mut field_symbols = Vec::with_capacity_in(field_layouts.len(), env.arena);
|
||||||
|
|
||||||
for (_, arg) in args.iter() {
|
for (_, arg) in args.iter() {
|
||||||
if let roc_can::expr::Expr::Var(symbol) = arg.value {
|
field_symbols.push(possible_reuse_symbol(env, procs, &arg.value));
|
||||||
field_symbols.push(symbol);
|
|
||||||
} else {
|
|
||||||
field_symbols.push(env.unique_symbol());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
let field_symbols = field_symbols.into_bump_slice();
|
||||||
|
|
||||||
// Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field
|
// Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field
|
||||||
let layout = layout_cache
|
let layout = layout_cache
|
||||||
|
@ -1379,30 +1461,10 @@ pub fn with_hole<'a>(
|
||||||
});
|
});
|
||||||
|
|
||||||
// even though this was originally a Tag, we treat it as a Struct from now on
|
// even though this was originally a Tag, we treat it as a Struct from now on
|
||||||
let mut stmt = Stmt::Let(
|
let stmt = Stmt::Let(assigned, Expr::Struct(field_symbols), layout, hole);
|
||||||
assigned,
|
|
||||||
Expr::Struct(field_symbols.clone().into_bump_slice()),
|
|
||||||
layout,
|
|
||||||
hole,
|
|
||||||
);
|
|
||||||
|
|
||||||
for ((_, arg), symbol) in args.into_iter().rev().zip(field_symbols.iter().rev())
|
let iter = args.into_iter().rev().zip(field_symbols.iter().rev());
|
||||||
{
|
assign_to_symbols(env, procs, layout_cache, iter, stmt)
|
||||||
// if this argument is already a symbol, we don't need to re-define it
|
|
||||||
if let roc_can::expr::Expr::Var(_) = arg.value {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
stmt = with_hole(
|
|
||||||
env,
|
|
||||||
arg.value,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
*symbol,
|
|
||||||
env.arena.alloc(stmt),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt
|
|
||||||
}
|
}
|
||||||
Wrapped(sorted_tag_layouts) => {
|
Wrapped(sorted_tag_layouts) => {
|
||||||
let union_size = sorted_tag_layouts.len() as u8;
|
let union_size = sorted_tag_layouts.len() as u8;
|
||||||
|
@ -1417,11 +1479,7 @@ pub fn with_hole<'a>(
|
||||||
field_symbols.push(tag_id_symbol);
|
field_symbols.push(tag_id_symbol);
|
||||||
|
|
||||||
for (_, arg) in args.iter() {
|
for (_, arg) in args.iter() {
|
||||||
if let roc_can::expr::Expr::Var(symbol) = arg.value {
|
field_symbols.push(possible_reuse_symbol(env, procs, &arg.value));
|
||||||
field_symbols.push(symbol);
|
|
||||||
} else {
|
|
||||||
field_symbols.push(env.unique_symbol());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut layouts: Vec<&'a [Layout<'a>]> =
|
let mut layouts: Vec<&'a [Layout<'a>]> =
|
||||||
|
@ -1442,23 +1500,9 @@ pub fn with_hole<'a>(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut stmt = Stmt::Let(assigned, tag, layout, hole);
|
let mut stmt = Stmt::Let(assigned, tag, layout, hole);
|
||||||
|
let iter = args.into_iter().rev().zip(field_symbols.iter().rev());
|
||||||
|
|
||||||
for ((_, arg), symbol) in args.into_iter().rev().zip(field_symbols.iter().rev())
|
stmt = assign_to_symbols(env, procs, layout_cache, iter, stmt);
|
||||||
{
|
|
||||||
// if this argument is already a symbol, we don't need to re-define it
|
|
||||||
if let roc_can::expr::Expr::Var(_) = arg.value {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt = with_hole(
|
|
||||||
env,
|
|
||||||
arg.value,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
*symbol,
|
|
||||||
env.arena.alloc(stmt),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// define the tag id
|
// define the tag id
|
||||||
stmt = Stmt::Let(
|
stmt = Stmt::Let(
|
||||||
|
@ -1487,16 +1531,18 @@ pub fn with_hole<'a>(
|
||||||
for (label, layout) in sorted_fields.into_iter() {
|
for (label, layout) in sorted_fields.into_iter() {
|
||||||
field_layouts.push(layout);
|
field_layouts.push(layout);
|
||||||
|
|
||||||
|
// TODO how should function pointers be handled here?
|
||||||
match fields.remove(&label) {
|
match fields.remove(&label) {
|
||||||
Some(field) => {
|
Some(field) => match can_reuse_symbol(procs, &field.loc_expr.value) {
|
||||||
if let roc_can::expr::Expr::Var(symbol) = field.loc_expr.value {
|
Some(reusable) => {
|
||||||
field_symbols.push(symbol);
|
field_symbols.push(reusable);
|
||||||
can_fields.push(None);
|
can_fields.push(None);
|
||||||
} else {
|
}
|
||||||
|
None => {
|
||||||
field_symbols.push(env.unique_symbol());
|
field_symbols.push(env.unique_symbol());
|
||||||
can_fields.push(Some(field));
|
can_fields.push(Some(field));
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
None => {
|
None => {
|
||||||
// this field was optional, but not given
|
// this field was optional, but not given
|
||||||
continue;
|
continue;
|
||||||
|
@ -1667,11 +1713,7 @@ pub fn with_hole<'a>(
|
||||||
loc_cond,
|
loc_cond,
|
||||||
branches,
|
branches,
|
||||||
} => {
|
} => {
|
||||||
let cond_symbol = if let roc_can::expr::Expr::Var(symbol) = loc_cond.value {
|
let cond_symbol = possible_reuse_symbol(env, procs, &loc_cond.value);
|
||||||
symbol
|
|
||||||
} else {
|
|
||||||
env.unique_symbol()
|
|
||||||
};
|
|
||||||
|
|
||||||
let id = JoinPointId(env.unique_symbol());
|
let id = JoinPointId(env.unique_symbol());
|
||||||
|
|
||||||
|
@ -1688,18 +1730,15 @@ pub fn with_hole<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
// define the `when` condition
|
// define the `when` condition
|
||||||
if let roc_can::expr::Expr::Var(_) = loc_cond.value {
|
stmt = assign_to_symbol(
|
||||||
// do nothing
|
env,
|
||||||
} else {
|
procs,
|
||||||
stmt = with_hole(
|
layout_cache,
|
||||||
env,
|
cond_var,
|
||||||
loc_cond.value,
|
*loc_cond,
|
||||||
procs,
|
cond_symbol,
|
||||||
layout_cache,
|
stmt,
|
||||||
cond_symbol,
|
);
|
||||||
env.arena.alloc(stmt),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
let layout = layout_cache
|
let layout = layout_cache
|
||||||
.from_var(env.arena, expr_var, env.subs)
|
.from_var(env.arena, expr_var, env.subs)
|
||||||
|
@ -1732,11 +1771,7 @@ pub fn with_hole<'a>(
|
||||||
} => {
|
} => {
|
||||||
let mut arg_symbols = Vec::with_capacity_in(loc_elems.len(), env.arena);
|
let mut arg_symbols = Vec::with_capacity_in(loc_elems.len(), env.arena);
|
||||||
for arg_expr in loc_elems.iter() {
|
for arg_expr in loc_elems.iter() {
|
||||||
if let roc_can::expr::Expr::Var(symbol) = arg_expr.value {
|
arg_symbols.push(possible_reuse_symbol(env, procs, &arg_expr.value));
|
||||||
arg_symbols.push(symbol);
|
|
||||||
} else {
|
|
||||||
arg_symbols.push(env.unique_symbol());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let arg_symbols = arg_symbols.into_bump_slice();
|
let arg_symbols = arg_symbols.into_bump_slice();
|
||||||
|
|
||||||
|
@ -1751,30 +1786,20 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
let mode = crate::layout::mode_from_var(list_var, env.subs);
|
let mode = crate::layout::mode_from_var(list_var, env.subs);
|
||||||
|
|
||||||
let mut stmt = Stmt::Let(
|
let stmt = Stmt::Let(
|
||||||
assigned,
|
assigned,
|
||||||
expr,
|
expr,
|
||||||
Layout::Builtin(Builtin::List(mode, env.arena.alloc(elem_layout))),
|
Layout::Builtin(Builtin::List(mode, env.arena.alloc(elem_layout))),
|
||||||
hole,
|
hole,
|
||||||
);
|
);
|
||||||
|
|
||||||
for (arg_expr, symbol) in loc_elems.into_iter().rev().zip(arg_symbols.iter().rev()) {
|
let iter = loc_elems
|
||||||
// if this argument is already a symbol, we don't need to re-define it
|
.into_iter()
|
||||||
if let roc_can::expr::Expr::Var(_) = arg_expr.value {
|
.rev()
|
||||||
continue;
|
.map(|e| (elem_var, e))
|
||||||
}
|
.zip(arg_symbols.iter().rev());
|
||||||
|
|
||||||
stmt = with_hole(
|
assign_to_symbols(env, procs, layout_cache, iter, stmt)
|
||||||
env,
|
|
||||||
arg_expr.value,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
*symbol,
|
|
||||||
env.arena.alloc(stmt),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
stmt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Access {
|
Access {
|
||||||
|
@ -1808,11 +1833,7 @@ pub fn with_hole<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let record_symbol = if let roc_can::expr::Expr::Var(symbol) = loc_expr.value {
|
let record_symbol = possible_reuse_symbol(env, procs, &loc_expr.value);
|
||||||
symbol
|
|
||||||
} else {
|
|
||||||
env.unique_symbol()
|
|
||||||
};
|
|
||||||
|
|
||||||
let expr = Expr::AccessAtIndex {
|
let expr = Expr::AccessAtIndex {
|
||||||
index: index.expect("field not in its own type") as u64,
|
index: index.expect("field not in its own type") as u64,
|
||||||
|
@ -1827,18 +1848,15 @@ pub fn with_hole<'a>(
|
||||||
|
|
||||||
let mut stmt = Stmt::Let(assigned, expr, layout, hole);
|
let mut stmt = Stmt::Let(assigned, expr, layout, hole);
|
||||||
|
|
||||||
if let roc_can::expr::Expr::Var(_) = loc_expr.value {
|
stmt = assign_to_symbol(
|
||||||
// do nothing
|
env,
|
||||||
} else {
|
procs,
|
||||||
stmt = with_hole(
|
layout_cache,
|
||||||
env,
|
record_var,
|
||||||
loc_expr.value,
|
*loc_expr,
|
||||||
procs,
|
record_symbol,
|
||||||
layout_cache,
|
stmt,
|
||||||
record_symbol,
|
);
|
||||||
env.arena.alloc(stmt),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
stmt
|
stmt
|
||||||
}
|
}
|
||||||
|
@ -1869,35 +1887,8 @@ pub fn with_hole<'a>(
|
||||||
Call(boxed, loc_args, _) => {
|
Call(boxed, loc_args, _) => {
|
||||||
let (fn_var, loc_expr, ret_var) = *boxed;
|
let (fn_var, loc_expr, ret_var) = *boxed;
|
||||||
|
|
||||||
/*
|
|
||||||
Var(symbol) => {
|
|
||||||
if procs.module_thunks.contains(&symbol) {
|
|
||||||
let partial_proc = procs.partial_procs.get(&symbol).unwrap();
|
|
||||||
let fn_var = partial_proc.annotation;
|
|
||||||
let ret_var = fn_var; // These are the same for a thunk.
|
|
||||||
|
|
||||||
// This is a top-level declaration, which will code gen to a 0-arity thunk.
|
|
||||||
call_by_name(
|
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
fn_var,
|
|
||||||
ret_var,
|
|
||||||
symbol,
|
|
||||||
std::vec::Vec::new(),
|
|
||||||
layout_cache,
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
// NOTE Load will always increment the refcount
|
|
||||||
Expr::Load(symbol)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
// match from_can(env, loc_expr.value, procs, layout_cache) {
|
// match from_can(env, loc_expr.value, procs, layout_cache) {
|
||||||
match loc_expr.value {
|
match loc_expr.value {
|
||||||
roc_can::expr::Expr::Var(proc_name) if procs.module_thunks.contains(&proc_name) => {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
roc_can::expr::Expr::Var(proc_name) => call_by_name(
|
roc_can::expr::Expr::Var(proc_name) => call_by_name(
|
||||||
env,
|
env,
|
||||||
procs,
|
procs,
|
||||||
|
@ -1993,11 +1984,7 @@ pub fn with_hole<'a>(
|
||||||
let mut arg_symbols = Vec::with_capacity_in(args.len(), env.arena);
|
let mut arg_symbols = Vec::with_capacity_in(args.len(), env.arena);
|
||||||
|
|
||||||
for (_, arg_expr) in args.iter() {
|
for (_, arg_expr) in args.iter() {
|
||||||
if let roc_can::expr::Expr::Var(symbol) = arg_expr {
|
arg_symbols.push(possible_reuse_symbol(env, procs, &arg_expr));
|
||||||
arg_symbols.push(*symbol);
|
|
||||||
} else {
|
|
||||||
arg_symbols.push(env.unique_symbol());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
let arg_symbols = arg_symbols.into_bump_slice();
|
let arg_symbols = arg_symbols.into_bump_slice();
|
||||||
|
|
||||||
|
@ -2006,27 +1993,14 @@ pub fn with_hole<'a>(
|
||||||
.from_var(env.arena, ret_var, env.subs)
|
.from_var(env.arena, ret_var, env.subs)
|
||||||
.unwrap_or_else(|err| todo!("TODO turn fn_var into a RuntimeError {:?}", err));
|
.unwrap_or_else(|err| todo!("TODO turn fn_var into a RuntimeError {:?}", err));
|
||||||
|
|
||||||
let mut result = Stmt::Let(assigned, Expr::RunLowLevel(op, arg_symbols), layout, hole);
|
let result = Stmt::Let(assigned, Expr::RunLowLevel(op, arg_symbols), layout, hole);
|
||||||
|
|
||||||
for ((_arg_var, arg_expr), symbol) in
|
let iter = args
|
||||||
args.into_iter().rev().zip(arg_symbols.iter().rev())
|
.into_iter()
|
||||||
{
|
.rev()
|
||||||
// if this argument is already a symbol, we don't need to re-define it
|
.map(|(a, b)| (a, Located::at_zero(b)))
|
||||||
if let roc_can::expr::Expr::Var(_) = arg_expr {
|
.zip(arg_symbols.iter().rev());
|
||||||
continue;
|
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||||
}
|
|
||||||
|
|
||||||
result = with_hole(
|
|
||||||
env,
|
|
||||||
arg_expr,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
*symbol,
|
|
||||||
env.arena.alloc(result),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
RuntimeError(e) => Stmt::RuntimeError(env.arena.alloc(format!("{:?}", e))),
|
RuntimeError(e) => Stmt::RuntimeError(env.arena.alloc(format!("{:?}", e))),
|
||||||
}
|
}
|
||||||
|
@ -2048,13 +2022,9 @@ pub fn from_can<'a>(
|
||||||
loc_cond,
|
loc_cond,
|
||||||
branches,
|
branches,
|
||||||
} => {
|
} => {
|
||||||
let cond_symbol = if let roc_can::expr::Expr::Var(symbol) = loc_cond.value {
|
let cond_symbol = possible_reuse_symbol(env, procs, &loc_cond.value);
|
||||||
symbol
|
|
||||||
} else {
|
|
||||||
env.unique_symbol()
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut stmt = from_can_when(
|
let stmt = from_can_when(
|
||||||
env,
|
env,
|
||||||
cond_var,
|
cond_var,
|
||||||
expr_var,
|
expr_var,
|
||||||
|
@ -2067,20 +2037,15 @@ pub fn from_can<'a>(
|
||||||
);
|
);
|
||||||
|
|
||||||
// define the `when` condition
|
// define the `when` condition
|
||||||
if let roc_can::expr::Expr::Var(_) = loc_cond.value {
|
assign_to_symbol(
|
||||||
// do nothing
|
env,
|
||||||
} else {
|
procs,
|
||||||
stmt = with_hole(
|
layout_cache,
|
||||||
env,
|
cond_var,
|
||||||
loc_cond.value,
|
*loc_cond,
|
||||||
procs,
|
cond_symbol,
|
||||||
layout_cache,
|
stmt,
|
||||||
cond_symbol,
|
)
|
||||||
env.arena.alloc(stmt),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
stmt
|
|
||||||
}
|
}
|
||||||
If {
|
If {
|
||||||
cond_var,
|
cond_var,
|
||||||
|
@ -2978,6 +2943,88 @@ fn store_record_destruct<'a>(
|
||||||
Ok(stmt)
|
Ok(stmt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// We want to re-use symbols that are not function symbols
|
||||||
|
/// for any other expression, we create a new symbol, and will
|
||||||
|
/// later make sure it gets assigned the correct value.
|
||||||
|
fn can_reuse_symbol<'a>(procs: &Procs<'a>, expr: &roc_can::expr::Expr) -> Option<Symbol> {
|
||||||
|
if let roc_can::expr::Expr::Var(symbol) = expr {
|
||||||
|
if procs.partial_procs.contains_key(&symbol) {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(*symbol)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn possible_reuse_symbol<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &Procs<'a>,
|
||||||
|
expr: &roc_can::expr::Expr,
|
||||||
|
) -> Symbol {
|
||||||
|
match can_reuse_symbol(procs, expr) {
|
||||||
|
Some(s) => s,
|
||||||
|
None => env.unique_symbol(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_to_symbol<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
arg_var: Variable,
|
||||||
|
loc_arg: Located<roc_can::expr::Expr>,
|
||||||
|
symbol: Symbol,
|
||||||
|
result: Stmt<'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 {
|
||||||
|
if procs.partial_procs.contains_key(&original) {
|
||||||
|
// 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");
|
||||||
|
procs.insert_passed_by_name(env, arg_var, original, layout.clone(), layout_cache);
|
||||||
|
|
||||||
|
return Stmt::Let(
|
||||||
|
symbol,
|
||||||
|
Expr::FunctionPointer(original, layout.clone()),
|
||||||
|
layout,
|
||||||
|
env.arena.alloc(result),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
with_hole(
|
||||||
|
env,
|
||||||
|
loc_arg.value,
|
||||||
|
procs,
|
||||||
|
layout_cache,
|
||||||
|
symbol,
|
||||||
|
env.arena.alloc(result),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn assign_to_symbols<'a, I>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
procs: &mut Procs<'a>,
|
||||||
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
iter: I,
|
||||||
|
mut result: Stmt<'a>,
|
||||||
|
) -> Stmt<'a>
|
||||||
|
where
|
||||||
|
I: Iterator<Item = ((Variable, Located<roc_can::expr::Expr>), &'a Symbol)>,
|
||||||
|
{
|
||||||
|
for ((arg_var, loc_arg), symbol) in iter {
|
||||||
|
result = assign_to_symbol(env, procs, layout_cache, arg_var, loc_arg, *symbol, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
fn call_by_name<'a>(
|
fn call_by_name<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
|
@ -2997,16 +3044,13 @@ fn call_by_name<'a>(
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
let mut pattern_vars = Vec::with_capacity_in(loc_args.len(), arena);
|
let mut pattern_vars = Vec::with_capacity_in(loc_args.len(), arena);
|
||||||
|
|
||||||
let mut field_symbols = Vec::with_capacity_in(loc_args.len(), env.arena);
|
let field_symbols = Vec::from_iter_in(
|
||||||
|
loc_args
|
||||||
for (_, arg_expr) in loc_args.iter() {
|
.iter()
|
||||||
if let roc_can::expr::Expr::Var(symbol) = arg_expr.value {
|
.map(|(_, arg_expr)| possible_reuse_symbol(env, procs, &arg_expr.value)),
|
||||||
field_symbols.push(symbol);
|
arena,
|
||||||
} else {
|
)
|
||||||
field_symbols.push(env.unique_symbol());
|
.into_bump_slice();
|
||||||
}
|
|
||||||
}
|
|
||||||
let field_symbols = field_symbols.into_bump_slice();
|
|
||||||
|
|
||||||
for (var, _) in &loc_args {
|
for (var, _) in &loc_args {
|
||||||
match layout_cache.from_var(&env.arena, *var, &env.subs) {
|
match layout_cache.from_var(&env.arena, *var, &env.subs) {
|
||||||
|
@ -3044,26 +3088,10 @@ fn call_by_name<'a>(
|
||||||
args: field_symbols,
|
args: field_symbols,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut result = Stmt::Let(assigned, call, ret_layout.clone(), hole);
|
let result = Stmt::Let(assigned, call, ret_layout.clone(), hole);
|
||||||
|
|
||||||
for ((_, loc_arg), symbol) in
|
let iter = loc_args.into_iter().rev().zip(field_symbols.iter().rev());
|
||||||
loc_args.into_iter().rev().zip(field_symbols.iter().rev())
|
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||||
{
|
|
||||||
// if this argument is already a symbol, we don't need to re-define it
|
|
||||||
if let roc_can::expr::Expr::Var(_) = loc_arg.value {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result = with_hole(
|
|
||||||
env,
|
|
||||||
loc_arg.value,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
*symbol,
|
|
||||||
env.arena.alloc(result),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
} else {
|
} else {
|
||||||
let pending = PendingSpecialization {
|
let pending = PendingSpecialization {
|
||||||
pattern_vars,
|
pattern_vars,
|
||||||
|
@ -3100,26 +3128,10 @@ fn call_by_name<'a>(
|
||||||
args: field_symbols,
|
args: field_symbols,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut result = Stmt::Let(assigned, call, ret_layout.clone(), hole);
|
let iter = loc_args.into_iter().rev().zip(field_symbols.iter().rev());
|
||||||
|
|
||||||
for ((_, loc_arg), symbol) in
|
let result = Stmt::Let(assigned, call, ret_layout.clone(), hole);
|
||||||
loc_args.into_iter().rev().zip(field_symbols.iter().rev())
|
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||||
{
|
|
||||||
// if this argument is already a symbol, we don't need to re-define it
|
|
||||||
if let roc_can::expr::Expr::Var(_) = loc_arg.value {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result = with_hole(
|
|
||||||
env,
|
|
||||||
loc_arg.value,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
*symbol,
|
|
||||||
env.arena.alloc(result),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
let opt_partial_proc = procs.partial_procs.get(&proc_name);
|
let opt_partial_proc = procs.partial_procs.get(&proc_name);
|
||||||
|
@ -3157,29 +3169,15 @@ fn call_by_name<'a>(
|
||||||
args: field_symbols,
|
args: field_symbols,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut result =
|
let iter = loc_args
|
||||||
Stmt::Let(assigned, call, ret_layout.clone(), hole);
|
|
||||||
|
|
||||||
for ((_, loc_arg), symbol) in loc_args
|
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.rev()
|
.rev()
|
||||||
.zip(field_symbols.iter().rev())
|
.zip(field_symbols.iter().rev());
|
||||||
{
|
|
||||||
// if this argument is already a symbol, we don't need to re-define it
|
|
||||||
if let roc_can::expr::Expr::Var(_) = loc_arg.value {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
result = with_hole(
|
|
||||||
env,
|
|
||||||
loc_arg.value,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
*symbol,
|
|
||||||
env.arena.alloc(result),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
result
|
let result =
|
||||||
|
Stmt::Let(assigned, call, ret_layout.clone(), hole);
|
||||||
|
|
||||||
|
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error_msg = env.arena.alloc(format!(
|
let error_msg = env.arena.alloc(format!(
|
||||||
|
|
|
@ -1528,4 +1528,57 @@ mod test_mono {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
#[test]
|
||||||
|
fn list_map() {
|
||||||
|
compiles_to_ir(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
main = \{} ->
|
||||||
|
nonEmpty : List Int
|
||||||
|
nonEmpty =
|
||||||
|
[ 1, 1, -4, 1, 2 ]
|
||||||
|
|
||||||
|
|
||||||
|
greaterThanOne : Int -> Bool
|
||||||
|
greaterThanOne = \i ->
|
||||||
|
i > 0
|
||||||
|
|
||||||
|
List.map nonEmpty greaterThanOne
|
||||||
|
|
||||||
|
main {}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
procedure Test.0 (Test.6):
|
||||||
|
let Test.15 = 1i64;
|
||||||
|
let Test.16 = 1i64;
|
||||||
|
let Test.17 = -4i64;
|
||||||
|
let Test.18 = 1i64;
|
||||||
|
let Test.19 = 2i64;
|
||||||
|
let Test.2 = Array [Test.15, Test.16, Test.17, Test.18, Test.19];
|
||||||
|
let Test.10 = FunctionPointer Test.3;
|
||||||
|
let Test.9 = CallByName List.6 Test.2 Test.10;
|
||||||
|
ret Test.9;
|
||||||
|
|
||||||
|
procedure List.6 (#Attr.2, #Attr.3):
|
||||||
|
let Test.11 = lowlevel ListMap #Attr.2 #Attr.3;
|
||||||
|
ret Test.11;
|
||||||
|
|
||||||
|
procedure Num.19 (#Attr.2, #Attr.3):
|
||||||
|
let Test.14 = lowlevel NumGt #Attr.2 #Attr.3;
|
||||||
|
ret Test.14;
|
||||||
|
|
||||||
|
procedure Test.3 (Test.5):
|
||||||
|
let Test.13 = 0i64;
|
||||||
|
let Test.12 = CallByName Num.19 Test.5 Test.13;
|
||||||
|
ret Test.12;
|
||||||
|
|
||||||
|
let Test.8 = Struct {};
|
||||||
|
let Test.7 = CallByName Test.0 Test.8;
|
||||||
|
ret Test.7;
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue