mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 16:21:11 +00:00
Merge remote-tracking branch 'origin/specialize-owned' into dict-more
This commit is contained in:
commit
32bba5206a
11 changed files with 478 additions and 282 deletions
|
@ -199,7 +199,7 @@ mod cli_run {
|
|||
"deriv",
|
||||
&[],
|
||||
"1 count: 6\n2 count: 22\n",
|
||||
false,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -241,7 +241,6 @@ mod cli_run {
|
|||
);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
#[serial(closure1)]
|
||||
fn closure1() {
|
||||
|
@ -254,7 +253,6 @@ mod cli_run {
|
|||
);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
#[serial(closure2)]
|
||||
fn closure2() {
|
||||
|
@ -267,7 +265,6 @@ mod cli_run {
|
|||
);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
#[serial(closure3)]
|
||||
fn closure3() {
|
||||
|
@ -280,7 +277,6 @@ mod cli_run {
|
|||
);
|
||||
}
|
||||
|
||||
#[ignore]
|
||||
#[test]
|
||||
#[serial(closure4)]
|
||||
fn closure4() {
|
||||
|
|
|
@ -928,7 +928,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
tag_layout: Layout::Union(UnionLayout::NonRecursive(fields)),
|
||||
union_size,
|
||||
tag_id,
|
||||
tag_name,
|
||||
..
|
||||
} => {
|
||||
let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
|
||||
|
@ -944,15 +943,10 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||
|
||||
let tag_field_layouts = if let TagName::Closure(_) = tag_name {
|
||||
// closures ignore (and do not store) the discriminant
|
||||
&fields[*tag_id as usize][1..]
|
||||
} else {
|
||||
&fields[*tag_id as usize]
|
||||
};
|
||||
let tag_field_layouts = &fields[*tag_id as usize];
|
||||
|
||||
for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) {
|
||||
let (val, val_layout) = load_symbol_and_layout(scope, field_symbol);
|
||||
let (val, _val_layout) = load_symbol_and_layout(scope, field_symbol);
|
||||
|
||||
// Zero-sized fields have no runtime representation.
|
||||
// The layout of the struct expects them to be dropped!
|
||||
|
@ -968,7 +962,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
);
|
||||
} else {
|
||||
// this check fails for recursive tag unions, but can be helpful while debugging
|
||||
debug_assert_eq!(tag_field_layout, val_layout);
|
||||
// debug_assert_eq!(tag_field_layout, val_layout);
|
||||
|
||||
field_vals.push(val);
|
||||
}
|
||||
|
@ -1025,7 +1019,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
tag_layout: Layout::Union(UnionLayout::Recursive(fields)),
|
||||
union_size,
|
||||
tag_id,
|
||||
tag_name,
|
||||
..
|
||||
} => {
|
||||
let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
|
||||
|
@ -1041,12 +1034,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||
|
||||
let tag_field_layouts = if let TagName::Closure(_) = tag_name {
|
||||
// closures ignore (and do not store) the discriminant
|
||||
&fields[*tag_id as usize][1..]
|
||||
} else {
|
||||
&fields[*tag_id as usize]
|
||||
};
|
||||
let tag_field_layouts = &fields[*tag_id as usize];
|
||||
|
||||
for (field_symbol, tag_field_layout) in arguments.iter().zip(tag_field_layouts.iter()) {
|
||||
let (val, val_layout) = load_symbol_and_layout(scope, field_symbol);
|
||||
|
@ -1189,7 +1177,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
}),
|
||||
union_size,
|
||||
tag_id,
|
||||
tag_name,
|
||||
..
|
||||
} => {
|
||||
let tag_layout = Layout::Union(UnionLayout::NonRecursive(fields));
|
||||
|
@ -1212,10 +1199,7 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
let mut field_types = Vec::with_capacity_in(num_fields, env.arena);
|
||||
let mut field_vals = Vec::with_capacity_in(num_fields, env.arena);
|
||||
|
||||
let tag_field_layouts = if let TagName::Closure(_) = tag_name {
|
||||
// closures ignore (and do not store) the discriminant
|
||||
&fields[*tag_id as usize][1..]
|
||||
} else {
|
||||
let tag_field_layouts = {
|
||||
use std::cmp::Ordering::*;
|
||||
match tag_id.cmp(&(*nullable_id as u8)) {
|
||||
Equal => &[] as &[_],
|
||||
|
@ -2225,14 +2209,6 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
|
||||
match modify {
|
||||
Inc(symbol, inc_amount) => {
|
||||
match cont {
|
||||
Refcounting(ModifyRc::Dec(symbol1), contcont)
|
||||
if *inc_amount == 1 && symbol == symbol1 =>
|
||||
{
|
||||
// the inc and dec cancel out
|
||||
build_exp_stmt(env, layout_ids, scope, parent, contcont)
|
||||
}
|
||||
_ => {
|
||||
let (value, layout) = load_symbol_and_layout(scope, symbol);
|
||||
let layout = layout.clone();
|
||||
|
||||
|
@ -2249,8 +2225,6 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
|||
|
||||
build_exp_stmt(env, layout_ids, scope, parent, cont)
|
||||
}
|
||||
}
|
||||
}
|
||||
Dec(symbol) => {
|
||||
let (value, layout) = load_symbol_and_layout(scope, symbol);
|
||||
|
||||
|
@ -3455,9 +3429,16 @@ fn function_value_by_name<'a, 'ctx, 'env>(
|
|||
if symbol.is_builtin() {
|
||||
panic!("Unrecognized builtin function: {:?}", fn_name)
|
||||
} else {
|
||||
panic!(
|
||||
"Unrecognized non-builtin function: {:?} (symbol: {:?}, layout: {:?})",
|
||||
// Unrecognized non-builtin function:
|
||||
eprintln!(
|
||||
"Unrecognized non-builtin function: {:?}\n\nSymbol: {:?}\nLayout: {:?}\n",
|
||||
fn_name, symbol, layout
|
||||
);
|
||||
eprintln!("Is the function defined? If so, maybe there is a problem with the layout");
|
||||
|
||||
panic!(
|
||||
"Unrecognized non-builtin function: {:?} (symbol: {:?})",
|
||||
fn_name, symbol,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -13,6 +13,7 @@ use inkwell::module::Linkage;
|
|||
use inkwell::types::{AnyTypeEnum, BasicType, BasicTypeEnum};
|
||||
use inkwell::values::{BasicValueEnum, FunctionValue, IntValue, PointerValue, StructValue};
|
||||
use inkwell::{AddressSpace, IntPredicate};
|
||||
use roc_module::symbol::Interns;
|
||||
use roc_module::symbol::Symbol;
|
||||
use roc_mono::layout::{Builtin, Layout, LayoutIds, MemoryMode, UnionLayout};
|
||||
|
||||
|
@ -515,14 +516,14 @@ fn modify_refcount_list<'a, 'ctx, 'env>(
|
|||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let (call_name, symbol) = match mode {
|
||||
Mode::Inc(_) => ("increment_list", Symbol::INC),
|
||||
Mode::Dec => ("decrement_list", Symbol::DEC),
|
||||
};
|
||||
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, &layout)
|
||||
.to_symbol_string(symbol, &env.interns);
|
||||
let (call_name, fn_name) = function_name_from_mode(
|
||||
layout_ids,
|
||||
&env.interns,
|
||||
"increment_list",
|
||||
"decrement_list",
|
||||
&layout,
|
||||
mode,
|
||||
);
|
||||
|
||||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
|
@ -614,14 +615,14 @@ fn modify_refcount_str<'a, 'ctx, 'env>(
|
|||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let (call_name, symbol) = match mode {
|
||||
Mode::Inc(_) => ("increment_str", Symbol::INC),
|
||||
Mode::Dec => ("decrement_str", Symbol::DEC),
|
||||
};
|
||||
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, &layout)
|
||||
.to_symbol_string(symbol, &env.interns);
|
||||
let (call_name, fn_name) = function_name_from_mode(
|
||||
layout_ids,
|
||||
&env.interns,
|
||||
"increment_str",
|
||||
"decrement_str",
|
||||
&layout,
|
||||
mode,
|
||||
);
|
||||
|
||||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
|
@ -712,14 +713,14 @@ fn modify_refcount_dict<'a, 'ctx, 'env>(
|
|||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let (call_name, symbol) = match mode {
|
||||
Mode::Inc(_) => ("increment_str", Symbol::INC),
|
||||
Mode::Dec => ("decrement_str", Symbol::DEC),
|
||||
};
|
||||
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, &layout)
|
||||
.to_symbol_string(symbol, &env.interns);
|
||||
let (call_name, fn_name) = function_name_from_mode(
|
||||
layout_ids,
|
||||
&env.interns,
|
||||
"increment_dict",
|
||||
"decrement_dict",
|
||||
&layout,
|
||||
mode,
|
||||
);
|
||||
|
||||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
|
@ -899,14 +900,14 @@ fn build_rec_union<'a, 'ctx, 'env>(
|
|||
) {
|
||||
let layout = Layout::Union(UnionLayout::Recursive(fields));
|
||||
|
||||
let (call_name, symbol) = match mode {
|
||||
Mode::Inc(_) => ("increment_rec_union", Symbol::INC),
|
||||
Mode::Dec => ("decrement_rec_union", Symbol::DEC),
|
||||
};
|
||||
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, &layout)
|
||||
.to_symbol_string(symbol, &env.interns);
|
||||
let (call_name, fn_name) = function_name_from_mode(
|
||||
layout_ids,
|
||||
&env.interns,
|
||||
"increment_rec_union",
|
||||
"decrement_rec_union",
|
||||
&layout,
|
||||
mode,
|
||||
);
|
||||
|
||||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
|
@ -1183,6 +1184,26 @@ fn call_help<'a, 'ctx, 'env>(
|
|||
call
|
||||
}
|
||||
|
||||
fn function_name_from_mode<'a>(
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
interns: &Interns,
|
||||
if_inc: &'static str,
|
||||
if_dec: &'static str,
|
||||
layout: &Layout<'a>,
|
||||
mode: Mode,
|
||||
) -> (&'static str, String) {
|
||||
// NOTE this is not a typo, we always determine the layout ID
|
||||
// using the DEC symbol. Anything that is incrementing must also be
|
||||
// decremented, so `dec` is used on more layouts. That can cause the
|
||||
// layout ids of the inc and dec versions to be different, which is
|
||||
// rather confusing, so now `inc_x` always corresponds to `dec_x`
|
||||
let layout_id = layout_ids.get(Symbol::DEC, layout);
|
||||
match mode {
|
||||
Mode::Inc(_) => (if_inc, layout_id.to_symbol_string(Symbol::INC, interns)),
|
||||
Mode::Dec => (if_dec, layout_id.to_symbol_string(Symbol::DEC, interns)),
|
||||
}
|
||||
}
|
||||
|
||||
fn modify_refcount_union<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
layout_ids: &mut LayoutIds<'a>,
|
||||
|
@ -1195,14 +1216,14 @@ fn modify_refcount_union<'a, 'ctx, 'env>(
|
|||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let di_location = env.builder.get_current_debug_location().unwrap();
|
||||
|
||||
let (call_name, symbol) = match mode {
|
||||
Mode::Inc(_) => ("increment_union", Symbol::INC),
|
||||
Mode::Dec => ("decrement_union", Symbol::DEC),
|
||||
};
|
||||
|
||||
let fn_name = layout_ids
|
||||
.get(symbol, &layout)
|
||||
.to_symbol_string(symbol, &env.interns);
|
||||
let (call_name, fn_name) = function_name_from_mode(
|
||||
layout_ids,
|
||||
&env.interns,
|
||||
"increment_union",
|
||||
"decrement_union",
|
||||
&layout,
|
||||
mode,
|
||||
);
|
||||
|
||||
let function = match env.module.get_function(fn_name.as_str()) {
|
||||
Some(function_value) => function_value,
|
||||
|
|
|
@ -751,7 +751,6 @@ enum Msg<'a> {
|
|||
layout_cache: LayoutCache<'a>,
|
||||
external_specializations_requested: MutMap<ModuleId, ExternalSpecializations>,
|
||||
procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
||||
passed_by_pointer: MutMap<(Symbol, Layout<'a>), Symbol>,
|
||||
problems: Vec<roc_mono::ir::MonoProblem>,
|
||||
module_timing: ModuleTiming,
|
||||
subs: Subs,
|
||||
|
@ -782,7 +781,6 @@ struct State<'a> {
|
|||
pub module_cache: ModuleCache<'a>,
|
||||
pub dependencies: Dependencies<'a>,
|
||||
pub procedures: MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
||||
pub passed_by_pointer: MutMap<(Symbol, Layout<'a>), Symbol>,
|
||||
pub exposed_to_host: MutMap<Symbol, Variable>,
|
||||
|
||||
/// This is the "final" list of IdentIds, after canonicalization and constraint gen
|
||||
|
@ -1405,7 +1403,6 @@ where
|
|||
module_cache: ModuleCache::default(),
|
||||
dependencies: Dependencies::default(),
|
||||
procedures: MutMap::default(),
|
||||
passed_by_pointer: MutMap::default(),
|
||||
exposed_to_host: MutMap::default(),
|
||||
exposed_types,
|
||||
headers_parsed,
|
||||
|
@ -1934,7 +1931,6 @@ fn update<'a>(
|
|||
mut ident_ids,
|
||||
subs,
|
||||
procedures,
|
||||
passed_by_pointer,
|
||||
external_specializations_requested,
|
||||
problems,
|
||||
module_timing,
|
||||
|
@ -1949,17 +1945,12 @@ fn update<'a>(
|
|||
.notify(module_id, Phase::MakeSpecializations);
|
||||
|
||||
state.procedures.extend(procedures);
|
||||
state.passed_by_pointer.extend(passed_by_pointer);
|
||||
state.timings.insert(module_id, module_timing);
|
||||
|
||||
if state.dependencies.solved_all() && state.goal_phase == Phase::MakeSpecializations {
|
||||
debug_assert!(work.is_empty(), "still work remaining {:?}", &work);
|
||||
|
||||
Proc::insert_refcount_operations(
|
||||
arena,
|
||||
&mut state.procedures,
|
||||
&state.passed_by_pointer,
|
||||
);
|
||||
Proc::insert_refcount_operations(arena, &mut state.procedures);
|
||||
|
||||
Proc::optimize_refcount_operations(
|
||||
arena,
|
||||
|
@ -3630,7 +3621,7 @@ fn make_specializations<'a>(
|
|||
);
|
||||
|
||||
let external_specializations_requested = procs.externals_we_need.clone();
|
||||
let (procedures, passed_by_pointer) = procs.get_specialized_procs_without_rc(mono_env.arena);
|
||||
let procedures = procs.get_specialized_procs_without_rc(mono_env.arena);
|
||||
|
||||
let make_specializations_end = SystemTime::now();
|
||||
module_timing.make_specializations = make_specializations_end
|
||||
|
@ -3642,7 +3633,6 @@ fn make_specializations<'a>(
|
|||
ident_ids,
|
||||
layout_cache,
|
||||
procedures,
|
||||
passed_by_pointer,
|
||||
problems: mono_problems,
|
||||
subs,
|
||||
external_specializations_requested,
|
||||
|
|
|
@ -16,23 +16,13 @@ fn should_borrow_layout(layout: &Layout) -> bool {
|
|||
pub fn infer_borrow<'a>(
|
||||
arena: &'a Bump,
|
||||
procs: &MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
||||
passed_by_pointer: &MutMap<(Symbol, Layout<'a>), Symbol>,
|
||||
) -> ParamMap<'a> {
|
||||
let mut param_map = ParamMap {
|
||||
items: MutMap::default(),
|
||||
};
|
||||
|
||||
for (key, other) in passed_by_pointer {
|
||||
if let Some(proc) = procs.get(key) {
|
||||
let mut proc: Proc = proc.clone();
|
||||
proc.name = *other;
|
||||
|
||||
param_map.visit_proc_always_owned(arena, &proc);
|
||||
}
|
||||
}
|
||||
|
||||
for proc in procs.values() {
|
||||
param_map.visit_proc(arena, proc);
|
||||
for (key, proc) in procs {
|
||||
param_map.visit_proc(arena, proc, key.clone());
|
||||
}
|
||||
|
||||
let mut env = BorrowInfState {
|
||||
|
@ -56,8 +46,8 @@ pub fn infer_borrow<'a>(
|
|||
// TODO in the future I think we need to do this properly, and group
|
||||
// mutually recursive functions (or just make all their arguments owned)
|
||||
|
||||
for proc in procs.values() {
|
||||
env.collect_proc(proc);
|
||||
for (key, proc) in procs {
|
||||
env.collect_proc(proc, key.1.clone());
|
||||
}
|
||||
|
||||
if !env.modified {
|
||||
|
@ -73,19 +63,19 @@ pub fn infer_borrow<'a>(
|
|||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq, Hash, Clone)]
|
||||
pub enum Key {
|
||||
Declaration(Symbol),
|
||||
pub enum Key<'a> {
|
||||
Declaration(Symbol, Layout<'a>),
|
||||
JoinPoint(JoinPointId),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct ParamMap<'a> {
|
||||
items: MutMap<Key, &'a [Param<'a>]>,
|
||||
items: MutMap<Key<'a>, &'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;
|
||||
type Item = (Key<'a>, &'a [Param<'a>]);
|
||||
type IntoIter = <std::collections::HashMap<Key<'a>, &'a [Param<'a>]> as IntoIterator>::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.items.into_iter()
|
||||
|
@ -93,8 +83,9 @@ impl<'a> IntoIterator for ParamMap<'a> {
|
|||
}
|
||||
|
||||
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;
|
||||
type Item = (&'a Key<'a>, &'a &'a [Param<'a>]);
|
||||
type IntoIter =
|
||||
<&'a std::collections::HashMap<Key<'a>, &'a [Param<'a>]> as IntoIterator>::IntoIter;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
self.items.iter()
|
||||
|
@ -102,8 +93,8 @@ impl<'a> IntoIterator for &'a ParamMap<'a> {
|
|||
}
|
||||
|
||||
impl<'a> ParamMap<'a> {
|
||||
pub fn get_symbol(&self, symbol: Symbol) -> Option<&'a [Param<'a>]> {
|
||||
let key = Key::Declaration(symbol);
|
||||
pub fn get_symbol(&self, symbol: Symbol, layout: Layout<'a>) -> Option<&'a [Param<'a>]> {
|
||||
let key = Key::Declaration(symbol, layout);
|
||||
|
||||
self.items.get(&key).copied()
|
||||
}
|
||||
|
@ -157,20 +148,31 @@ impl<'a> ParamMap<'a> {
|
|||
.into_bump_slice()
|
||||
}
|
||||
|
||||
fn visit_proc(&mut self, arena: &'a Bump, proc: &Proc<'a>) {
|
||||
self.items.insert(
|
||||
Key::Declaration(proc.name),
|
||||
fn visit_proc(&mut self, arena: &'a Bump, proc: &Proc<'a>, key: (Symbol, Layout<'a>)) {
|
||||
if proc.must_own_arguments {
|
||||
self.visit_proc_always_owned(arena, proc, key);
|
||||
return;
|
||||
}
|
||||
let already_in_there = self.items.insert(
|
||||
Key::Declaration(proc.name, key.1),
|
||||
Self::init_borrow_args(arena, proc.args),
|
||||
);
|
||||
debug_assert!(already_in_there.is_none());
|
||||
|
||||
self.visit_stmt(arena, proc.name, &proc.body);
|
||||
}
|
||||
|
||||
fn visit_proc_always_owned(&mut self, arena: &'a Bump, proc: &Proc<'a>) {
|
||||
self.items.insert(
|
||||
Key::Declaration(proc.name),
|
||||
fn visit_proc_always_owned(
|
||||
&mut self,
|
||||
arena: &'a Bump,
|
||||
proc: &Proc<'a>,
|
||||
key: (Symbol, Layout<'a>),
|
||||
) {
|
||||
let already_in_there = self.items.insert(
|
||||
Key::Declaration(proc.name, key.1),
|
||||
Self::init_borrow_args_always_owned(arena, proc.args),
|
||||
);
|
||||
debug_assert!(already_in_there.is_none());
|
||||
|
||||
self.visit_stmt(arena, proc.name, &proc.body);
|
||||
}
|
||||
|
@ -188,8 +190,10 @@ impl<'a> ParamMap<'a> {
|
|||
remainder: v,
|
||||
continuation: b,
|
||||
} => {
|
||||
self.items
|
||||
let already_in_there = self
|
||||
.items
|
||||
.insert(Key::JoinPoint(*j), Self::init_borrow_params(arena, xs));
|
||||
debug_assert!(already_in_there.is_none());
|
||||
|
||||
stack.push(v);
|
||||
stack.push(b);
|
||||
|
@ -252,7 +256,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn update_param_map(&mut self, k: Key) {
|
||||
fn update_param_map(&mut self, k: Key<'a>) {
|
||||
let arena = self.arena;
|
||||
if let Some(ps) = self.param_map.items.get(&k) {
|
||||
let ps = Vec::from_iter_in(
|
||||
|
@ -359,9 +363,11 @@ impl<'a> BorrowInfState<'a> {
|
|||
} = e;
|
||||
|
||||
match call_type {
|
||||
ByName { name, .. } => {
|
||||
ByName {
|
||||
name, full_layout, ..
|
||||
} => {
|
||||
// get the borrow signature of the applied function
|
||||
match self.param_map.get_symbol(*name) {
|
||||
match self.param_map.get_symbol(*name, full_layout.clone()) {
|
||||
Some(ps) => {
|
||||
// the return value will be owned
|
||||
self.own_var(z);
|
||||
|
@ -458,7 +464,12 @@ impl<'a> BorrowInfState<'a> {
|
|||
match (v, b) {
|
||||
(
|
||||
Expr::Call(crate::ir::Call {
|
||||
call_type: crate::ir::CallType::ByName { name: g, .. },
|
||||
call_type:
|
||||
crate::ir::CallType::ByName {
|
||||
name: g,
|
||||
full_layout,
|
||||
..
|
||||
},
|
||||
arguments: ys,
|
||||
..
|
||||
}),
|
||||
|
@ -466,7 +477,12 @@ impl<'a> BorrowInfState<'a> {
|
|||
)
|
||||
| (
|
||||
Expr::Call(crate::ir::Call {
|
||||
call_type: crate::ir::CallType::ByPointer { name: g, .. },
|
||||
call_type:
|
||||
crate::ir::CallType::ByPointer {
|
||||
name: g,
|
||||
full_layout,
|
||||
..
|
||||
},
|
||||
arguments: ys,
|
||||
..
|
||||
}),
|
||||
|
@ -475,7 +491,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
if self.current_proc == *g && x == *z {
|
||||
// anonymous functions (for which the ps may not be known)
|
||||
// can never be tail-recursive, so this is fine
|
||||
if let Some(ps) = self.param_map.get_symbol(*g) {
|
||||
if let Some(ps) = self.param_map.get_symbol(*g, full_layout.clone()) {
|
||||
self.own_params_using_args(ys, ps)
|
||||
}
|
||||
}
|
||||
|
@ -517,8 +533,10 @@ impl<'a> BorrowInfState<'a> {
|
|||
|
||||
Let(x, Expr::FunctionPointer(fsymbol, layout), _, b) => {
|
||||
// ensure that the function pointed to is in the param map
|
||||
if let Some(params) = self.param_map.get_symbol(*fsymbol) {
|
||||
self.param_map.items.insert(Key::Declaration(*x), params);
|
||||
if let Some(params) = self.param_map.get_symbol(*fsymbol, layout.clone()) {
|
||||
self.param_map
|
||||
.items
|
||||
.insert(Key::Declaration(*x, layout.clone()), params);
|
||||
}
|
||||
|
||||
self.collect_stmt(b);
|
||||
|
@ -574,7 +592,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
fn collect_proc(&mut self, proc: &Proc<'a>) {
|
||||
fn collect_proc(&mut self, proc: &Proc<'a>, layout: Layout<'a>) {
|
||||
let old = self.param_set.clone();
|
||||
|
||||
let ys = Vec::from_iter_in(proc.args.iter().map(|t| t.1), self.arena).into_bump_slice();
|
||||
|
@ -585,7 +603,7 @@ impl<'a> BorrowInfState<'a> {
|
|||
self.owned.entry(proc.name).or_default();
|
||||
|
||||
self.collect_stmt(&proc.body);
|
||||
self.update_param_map(Key::Declaration(proc.name));
|
||||
self.update_param_map(Key::Declaration(proc.name, layout));
|
||||
|
||||
self.param_set = old;
|
||||
}
|
||||
|
|
|
@ -232,7 +232,7 @@ impl<'a> Context<'a> {
|
|||
let mut vars = MutMap::default();
|
||||
|
||||
for (key, _) in param_map.into_iter() {
|
||||
if let crate::borrow::Key::Declaration(symbol) = key {
|
||||
if let crate::borrow::Key::Declaration(symbol, _) = key {
|
||||
vars.insert(
|
||||
*symbol,
|
||||
VarInfo {
|
||||
|
@ -466,9 +466,11 @@ impl<'a> Context<'a> {
|
|||
&*self.arena.alloc(Stmt::Let(z, v, l, b))
|
||||
}
|
||||
|
||||
ByName { name, .. } => {
|
||||
ByName {
|
||||
name, full_layout, ..
|
||||
} => {
|
||||
// get the borrow signature
|
||||
match self.param_map.get_symbol(*name) {
|
||||
match self.param_map.get_symbol(*name, full_layout.clone()) {
|
||||
Some(ps) => {
|
||||
let v = Expr::Call(crate::ir::Call {
|
||||
call_type,
|
||||
|
@ -601,11 +603,8 @@ impl<'a> Context<'a> {
|
|||
persistent: bool,
|
||||
consume: bool,
|
||||
) -> Self {
|
||||
// can this type be reference-counted at runtime?
|
||||
let reference = match layout {
|
||||
Layout::Closure(_, closure, _) => closure.layout.contains_refcounted(),
|
||||
_ => layout.contains_refcounted(),
|
||||
};
|
||||
// should we perform incs and decs on this value?
|
||||
let reference = layout.contains_refcounted();
|
||||
|
||||
let info = VarInfo {
|
||||
reference,
|
||||
|
@ -1005,10 +1004,15 @@ pub fn visit_declaration<'a>(
|
|||
ctx.add_dec_for_dead_params(params, b, &b_live_vars)
|
||||
}
|
||||
|
||||
pub fn visit_proc<'a>(arena: &'a Bump, param_map: &'a ParamMap<'a>, proc: &mut Proc<'a>) {
|
||||
pub fn visit_proc<'a>(
|
||||
arena: &'a Bump,
|
||||
param_map: &'a ParamMap<'a>,
|
||||
proc: &mut Proc<'a>,
|
||||
layout: Layout<'a>,
|
||||
) {
|
||||
let ctx = Context::new(arena, param_map);
|
||||
|
||||
let params = match param_map.get_symbol(proc.name) {
|
||||
let params = match param_map.get_symbol(proc.name, layout) {
|
||||
Some(slice) => slice,
|
||||
None => Vec::from_iter_in(
|
||||
proc.args.iter().cloned().map(|(layout, symbol)| Param {
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
use self::InProgressProc::*;
|
||||
use crate::exhaustive::{Ctor, Guard, RenderAs, TagId};
|
||||
use crate::layout::{
|
||||
Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem, UnionLayout, WrappedVariant,
|
||||
TAG_SIZE,
|
||||
BuildClosureData, Builtin, ClosureLayout, Layout, LayoutCache, LayoutProblem, UnionLayout,
|
||||
WrappedVariant, TAG_SIZE,
|
||||
};
|
||||
use bumpalo::collections::Vec;
|
||||
use bumpalo::Bump;
|
||||
|
@ -118,6 +118,7 @@ pub struct Proc<'a> {
|
|||
pub closure_data_layout: Option<Layout<'a>>,
|
||||
pub ret_layout: Layout<'a>,
|
||||
pub is_self_recursive: SelfRecursive,
|
||||
pub must_own_arguments: bool,
|
||||
pub host_exposed_layouts: HostExposedLayouts<'a>,
|
||||
}
|
||||
|
||||
|
@ -195,31 +196,11 @@ impl<'a> Proc<'a> {
|
|||
pub fn insert_refcount_operations(
|
||||
arena: &'a Bump,
|
||||
procs: &mut MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
||||
_passed_by_pointer: &MutMap<(Symbol, Layout<'a>), Symbol>,
|
||||
) {
|
||||
// currently we ignore the passed-by-pointerness
|
||||
let passed_by_pointer = &Default::default();
|
||||
let borrow_params = arena.alloc(crate::borrow::infer_borrow(arena, procs));
|
||||
|
||||
let borrow_params =
|
||||
arena.alloc(crate::borrow::infer_borrow(arena, procs, passed_by_pointer));
|
||||
|
||||
for (key, other) in passed_by_pointer {
|
||||
if let Some(proc) = procs.get(key) {
|
||||
let mut proc: Proc = proc.clone();
|
||||
proc.name = *other;
|
||||
|
||||
let layout = key.1.clone();
|
||||
procs.insert((*other, layout), proc);
|
||||
} else {
|
||||
unreachable!(
|
||||
"we need a by-pointer version of {:?}, but its by-name version does not exist",
|
||||
key.0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (_, proc) in procs.iter_mut() {
|
||||
crate::inc_dec::visit_proc(arena, borrow_params, proc);
|
||||
for (key, proc) in procs.iter_mut() {
|
||||
crate::inc_dec::visit_proc(arena, borrow_params, proc, key.1.clone());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -298,7 +279,6 @@ pub struct Procs<'a> {
|
|||
pub runtime_errors: MutMap<Symbol, &'a str>,
|
||||
pub externals_others_need: ExternalSpecializations,
|
||||
pub externals_we_need: MutMap<ModuleId, ExternalSpecializations>,
|
||||
pub passed_by_pointer: MutMap<(Symbol, Layout<'a>), Symbol>,
|
||||
}
|
||||
|
||||
impl<'a> Default for Procs<'a> {
|
||||
|
@ -311,7 +291,6 @@ impl<'a> Default for Procs<'a> {
|
|||
runtime_errors: MutMap::default(),
|
||||
externals_we_need: MutMap::default(),
|
||||
externals_others_need: ExternalSpecializations::default(),
|
||||
passed_by_pointer: MutMap::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -356,14 +335,10 @@ impl<'a> Procs<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub fn get_specialized_procs_without_rc(
|
||||
self,
|
||||
arena: &'a Bump,
|
||||
) -> (
|
||||
MutMap<(Symbol, Layout<'a>), Proc<'a>>,
|
||||
MutMap<(Symbol, Layout<'a>), Symbol>,
|
||||
) {
|
||||
) -> MutMap<(Symbol, Layout<'a>), Proc<'a>> {
|
||||
let mut result = MutMap::with_capacity_and_hasher(self.specialized.len(), default_hasher());
|
||||
|
||||
for (key, in_prog_proc) in self.specialized.into_iter() {
|
||||
|
@ -386,7 +361,7 @@ impl<'a> Procs<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
(result, self.passed_by_pointer)
|
||||
result
|
||||
}
|
||||
|
||||
// TODO investigate make this an iterator?
|
||||
|
@ -415,14 +390,10 @@ impl<'a> Procs<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
let borrow_params = arena.alloc(crate::borrow::infer_borrow(
|
||||
arena,
|
||||
&result,
|
||||
&self.passed_by_pointer,
|
||||
));
|
||||
let borrow_params = arena.alloc(crate::borrow::infer_borrow(arena, &result));
|
||||
|
||||
for (_, proc) in result.iter_mut() {
|
||||
crate::inc_dec::visit_proc(arena, borrow_params, proc);
|
||||
for (key, proc) in result.iter_mut() {
|
||||
crate::inc_dec::visit_proc(arena, borrow_params, proc, key.1.clone());
|
||||
}
|
||||
|
||||
result
|
||||
|
@ -459,14 +430,10 @@ impl<'a> Procs<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
let borrow_params = arena.alloc(crate::borrow::infer_borrow(
|
||||
arena,
|
||||
&result,
|
||||
&self.passed_by_pointer,
|
||||
));
|
||||
let borrow_params = arena.alloc(crate::borrow::infer_borrow(arena, &result));
|
||||
|
||||
for (_, proc) in result.iter_mut() {
|
||||
crate::inc_dec::visit_proc(arena, borrow_params, proc);
|
||||
for (key, proc) in result.iter_mut() {
|
||||
crate::inc_dec::visit_proc(arena, borrow_params, proc, key.1.clone());
|
||||
}
|
||||
|
||||
(result, borrow_params)
|
||||
|
@ -1935,6 +1902,7 @@ fn specialize_external<'a>(
|
|||
closure_data_layout,
|
||||
ret_layout: full_layout,
|
||||
is_self_recursive: recursivity,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts,
|
||||
};
|
||||
|
||||
|
@ -1962,12 +1930,14 @@ fn specialize_external<'a>(
|
|||
|
||||
match tag_layout {
|
||||
Layout::Struct(field_layouts) => {
|
||||
// NOTE closure unions do not store the tag!
|
||||
let field_layouts = &field_layouts[1..];
|
||||
|
||||
// TODO check for field_layouts.len() == 1 and do a rename in that case?
|
||||
for (index, (symbol, _variable)) in captured.iter().enumerate()
|
||||
for (mut index, (symbol, _variable)) in
|
||||
captured.iter().enumerate()
|
||||
{
|
||||
// the field layouts do store the tag, but the tag value is
|
||||
// not captured. So we drop the layout of the tag ID here
|
||||
index += 1;
|
||||
|
||||
// TODO therefore should the wrapped here not be RecordOrSingleTagUnion?
|
||||
let expr = Expr::AccessAtIndex {
|
||||
index: index as _,
|
||||
|
@ -2069,6 +2039,7 @@ fn specialize_external<'a>(
|
|||
closure_data_layout,
|
||||
ret_layout,
|
||||
is_self_recursive: recursivity,
|
||||
must_own_arguments: false,
|
||||
host_exposed_layouts,
|
||||
};
|
||||
|
||||
|
@ -2789,8 +2760,7 @@ pub fn with_hole<'a>(
|
|||
);
|
||||
|
||||
let outer_symbol = env.unique_symbol();
|
||||
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||
.unwrap();
|
||||
stmt = store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt);
|
||||
|
||||
// convert the def body, store in outer_symbol
|
||||
with_hole(
|
||||
|
@ -3762,8 +3732,12 @@ pub fn with_hole<'a>(
|
|||
.into_bump_slice();
|
||||
|
||||
// define the closure data, unless it's a basic unwrapped type already
|
||||
match closure_layout.build_closure_data(name, symbols) {
|
||||
Ok(expr) => {
|
||||
match closure_layout.build_closure_data(name, &symbols) {
|
||||
BuildClosureData::Alias(current) => {
|
||||
// there is only one symbol captured, use that immediately
|
||||
substitute_in_exprs(env.arena, &mut stmt, closure_data, current);
|
||||
}
|
||||
BuildClosureData::Struct(expr) => {
|
||||
stmt = Stmt::Let(
|
||||
closure_data,
|
||||
expr,
|
||||
|
@ -3771,9 +3745,40 @@ pub fn with_hole<'a>(
|
|||
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);
|
||||
BuildClosureData::Union {
|
||||
tag_id,
|
||||
tag_layout,
|
||||
union_size,
|
||||
tag_name,
|
||||
} => {
|
||||
let tag_id_symbol = env.unique_symbol();
|
||||
let mut tag_symbols =
|
||||
Vec::with_capacity_in(symbols.len() + 1, env.arena);
|
||||
tag_symbols.push(tag_id_symbol);
|
||||
tag_symbols.extend(symbols);
|
||||
|
||||
let expr1 = Expr::Literal(Literal::Int(tag_id as i64));
|
||||
let expr2 = Expr::Tag {
|
||||
tag_id,
|
||||
tag_layout,
|
||||
union_size,
|
||||
tag_name,
|
||||
arguments: tag_symbols.into_bump_slice(),
|
||||
};
|
||||
|
||||
stmt = Stmt::Let(
|
||||
closure_data,
|
||||
expr2,
|
||||
closure_data_layout.clone(),
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
|
||||
stmt = Stmt::Let(
|
||||
tag_id_symbol,
|
||||
expr1,
|
||||
Layout::Builtin(Builtin::Int64),
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4517,12 +4522,10 @@ pub fn from_can<'a>(
|
|||
|
||||
if let roc_can::expr::Expr::Var(outer_symbol) = def.loc_expr.value {
|
||||
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||
.unwrap()
|
||||
} else {
|
||||
let outer_symbol = env.unique_symbol();
|
||||
stmt =
|
||||
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt)
|
||||
.unwrap();
|
||||
store_pattern(env, procs, layout_cache, &mono_pattern, outer_symbol, stmt);
|
||||
|
||||
// convert the def body, store in outer_symbol
|
||||
with_hole(
|
||||
|
@ -4733,8 +4736,9 @@ fn from_can_when<'a>(
|
|||
jump,
|
||||
);
|
||||
|
||||
match store_pattern(env, procs, layout_cache, &pattern, cond_symbol, guard_stmt) {
|
||||
Ok(new_guard_stmt) => (
|
||||
let new_guard_stmt =
|
||||
store_pattern(env, procs, layout_cache, &pattern, cond_symbol, guard_stmt);
|
||||
(
|
||||
pattern,
|
||||
Guard::Guard {
|
||||
id,
|
||||
|
@ -4742,22 +4746,11 @@ fn from_can_when<'a>(
|
|||
stmt: new_guard_stmt,
|
||||
},
|
||||
branch_stmt,
|
||||
),
|
||||
Err(msg) => (
|
||||
Pattern::Underscore,
|
||||
Guard::NoGuard,
|
||||
Stmt::RuntimeError(env.arena.alloc(msg)),
|
||||
),
|
||||
}
|
||||
)
|
||||
} else {
|
||||
match store_pattern(env, procs, layout_cache, &pattern, cond_symbol, branch_stmt) {
|
||||
Ok(new_branch_stmt) => (pattern, Guard::NoGuard, new_branch_stmt),
|
||||
Err(msg) => (
|
||||
Pattern::Underscore,
|
||||
Guard::NoGuard,
|
||||
Stmt::RuntimeError(env.arena.alloc(msg)),
|
||||
),
|
||||
}
|
||||
let new_branch_stmt =
|
||||
store_pattern(env, procs, layout_cache, &pattern, cond_symbol, branch_stmt);
|
||||
(pattern, Guard::NoGuard, new_branch_stmt)
|
||||
}
|
||||
});
|
||||
let mono_branches = Vec::from_iter_in(it, arena);
|
||||
|
@ -5142,13 +5135,36 @@ fn substitute_in_expr<'a>(
|
|||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn store_pattern<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
can_pat: &Pattern<'a>,
|
||||
outer_symbol: Symbol,
|
||||
stmt: Stmt<'a>,
|
||||
) -> Stmt<'a> {
|
||||
match store_pattern_help(env, procs, layout_cache, can_pat, outer_symbol, stmt) {
|
||||
StorePattern::Productive(new) => new,
|
||||
StorePattern::NotProductive(new) => new,
|
||||
}
|
||||
}
|
||||
|
||||
enum StorePattern<'a> {
|
||||
/// we bound new symbols
|
||||
Productive(Stmt<'a>),
|
||||
/// no new symbols were bound in this pattern
|
||||
NotProductive(Stmt<'a>),
|
||||
}
|
||||
|
||||
/// It is crucial for correct RC insertion that we don't create dead variables!
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn store_pattern_help<'a>(
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
can_pat: &Pattern<'a>,
|
||||
outer_symbol: Symbol,
|
||||
mut stmt: Stmt<'a>,
|
||||
) -> Result<Stmt<'a>, &'a str> {
|
||||
) -> StorePattern<'a> {
|
||||
use Pattern::*;
|
||||
|
||||
match can_pat {
|
||||
|
@ -5157,19 +5173,26 @@ fn store_pattern<'a>(
|
|||
}
|
||||
Underscore => {
|
||||
// do nothing
|
||||
return StorePattern::NotProductive(stmt);
|
||||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {}
|
||||
| StrLiteral(_) => {
|
||||
return StorePattern::NotProductive(stmt);
|
||||
}
|
||||
AppliedTag {
|
||||
arguments, layout, ..
|
||||
arguments,
|
||||
layout,
|
||||
tag_name,
|
||||
..
|
||||
} => {
|
||||
let wrapped = Wrapped::from_layout(layout);
|
||||
let write_tag = wrapped == Wrapped::MultiTagUnion;
|
||||
|
||||
let mut arg_layouts = Vec::with_capacity_in(arguments.len(), env.arena);
|
||||
let mut is_productive = false;
|
||||
|
||||
if write_tag {
|
||||
// add an element for the tag discriminant
|
||||
|
@ -5200,6 +5223,7 @@ fn store_pattern<'a>(
|
|||
Identifier(symbol) => {
|
||||
// store immediately in the given symbol
|
||||
stmt = Stmt::Let(*symbol, load, arg_layout.clone(), env.arena.alloc(stmt));
|
||||
is_productive = true;
|
||||
}
|
||||
Underscore => {
|
||||
// ignore
|
||||
|
@ -5214,17 +5238,42 @@ fn store_pattern<'a>(
|
|||
let symbol = env.unique_symbol();
|
||||
|
||||
// first recurse, continuing to unpack symbol
|
||||
stmt = store_pattern(env, procs, layout_cache, argument, symbol, stmt)?;
|
||||
match store_pattern_help(env, procs, layout_cache, argument, symbol, stmt) {
|
||||
StorePattern::Productive(new) => {
|
||||
is_productive = true;
|
||||
println!(
|
||||
"Access {:?}.{:?} {:?}",
|
||||
tag_name.clone(),
|
||||
outer_symbol,
|
||||
index
|
||||
);
|
||||
stmt = new;
|
||||
// only if we bind one of its (sub)fields to a used name should we
|
||||
// extract the field
|
||||
stmt = Stmt::Let(
|
||||
symbol,
|
||||
load,
|
||||
arg_layout.clone(),
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
}
|
||||
StorePattern::NotProductive(new) => {
|
||||
// do nothing
|
||||
stmt = new;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then store the symbol
|
||||
stmt = Stmt::Let(symbol, load, arg_layout.clone(), env.arena.alloc(stmt));
|
||||
}
|
||||
}
|
||||
if !is_productive {
|
||||
return StorePattern::NotProductive(stmt);
|
||||
}
|
||||
}
|
||||
RecordDestructure(destructs, Layout::Struct(sorted_fields)) => {
|
||||
let mut is_productive = false;
|
||||
for (index, destruct) in destructs.iter().enumerate().rev() {
|
||||
stmt = store_record_destruct(
|
||||
match store_record_destruct(
|
||||
env,
|
||||
procs,
|
||||
layout_cache,
|
||||
|
@ -5233,7 +5282,19 @@ fn store_pattern<'a>(
|
|||
outer_symbol,
|
||||
sorted_fields,
|
||||
stmt,
|
||||
)?;
|
||||
) {
|
||||
StorePattern::Productive(new) => {
|
||||
is_productive = true;
|
||||
stmt = new;
|
||||
}
|
||||
StorePattern::NotProductive(new) => {
|
||||
stmt = new;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !is_productive {
|
||||
return StorePattern::NotProductive(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5242,7 +5303,7 @@ fn store_pattern<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
Ok(stmt)
|
||||
StorePattern::Productive(stmt)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
|
@ -5255,7 +5316,7 @@ fn store_record_destruct<'a>(
|
|||
outer_symbol: Symbol,
|
||||
sorted_fields: &'a [Layout<'a>],
|
||||
mut stmt: Stmt<'a>,
|
||||
) -> Result<Stmt<'a>, &'a str> {
|
||||
) -> StorePattern<'a> {
|
||||
use Pattern::*;
|
||||
|
||||
let wrapped = Wrapped::from_layout(&Layout::Struct(sorted_fields));
|
||||
|
@ -5298,24 +5359,32 @@ fn store_record_destruct<'a>(
|
|||
// { x, y: _ } -> ...
|
||||
//
|
||||
// internally. But `y` is never used, so we must make sure it't not stored/loaded.
|
||||
return StorePattern::NotProductive(stmt);
|
||||
}
|
||||
IntLiteral(_)
|
||||
| FloatLiteral(_)
|
||||
| EnumLiteral { .. }
|
||||
| BitLiteral { .. }
|
||||
| StrLiteral(_) => {}
|
||||
| StrLiteral(_) => {
|
||||
return StorePattern::NotProductive(stmt);
|
||||
}
|
||||
|
||||
_ => {
|
||||
let symbol = env.unique_symbol();
|
||||
|
||||
stmt = store_pattern(env, procs, layout_cache, guard_pattern, symbol, stmt)?;
|
||||
|
||||
stmt = Stmt::Let(symbol, load, destruct.layout.clone(), env.arena.alloc(stmt));
|
||||
match store_pattern_help(env, procs, layout_cache, guard_pattern, symbol, stmt) {
|
||||
StorePattern::Productive(new) => {
|
||||
stmt = new;
|
||||
stmt =
|
||||
Stmt::Let(symbol, load, destruct.layout.clone(), env.arena.alloc(stmt));
|
||||
}
|
||||
StorePattern::NotProductive(stmt) => return StorePattern::NotProductive(stmt),
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
Ok(stmt)
|
||||
StorePattern::Productive(stmt)
|
||||
}
|
||||
|
||||
/// We want to re-use symbols that are not function symbols
|
||||
|
@ -5515,8 +5584,12 @@ fn reuse_function_symbol<'a>(
|
|||
};
|
||||
|
||||
// define the closure data, unless it's a basic unwrapped type already
|
||||
match closure_layout.build_closure_data(original, symbols) {
|
||||
Ok(expr) => {
|
||||
match closure_layout.build_closure_data(original, &symbols) {
|
||||
BuildClosureData::Alias(current) => {
|
||||
// there is only one symbol captured, use that immediately
|
||||
substitute_in_exprs(env.arena, &mut stmt, closure_data, current);
|
||||
}
|
||||
BuildClosureData::Struct(expr) => {
|
||||
stmt = Stmt::Let(
|
||||
closure_data,
|
||||
expr,
|
||||
|
@ -5524,9 +5597,40 @@ fn reuse_function_symbol<'a>(
|
|||
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);
|
||||
BuildClosureData::Union {
|
||||
tag_id,
|
||||
tag_layout,
|
||||
union_size,
|
||||
tag_name,
|
||||
} => {
|
||||
let tag_id_symbol = env.unique_symbol();
|
||||
let mut tag_symbols =
|
||||
Vec::with_capacity_in(symbols.len() + 1, env.arena);
|
||||
tag_symbols.push(tag_id_symbol);
|
||||
tag_symbols.extend(symbols);
|
||||
|
||||
let expr1 = Expr::Literal(Literal::Int(tag_id as i64));
|
||||
let expr2 = Expr::Tag {
|
||||
tag_id,
|
||||
tag_layout,
|
||||
union_size,
|
||||
tag_name,
|
||||
arguments: tag_symbols.into_bump_slice(),
|
||||
};
|
||||
|
||||
stmt = Stmt::Let(
|
||||
closure_data,
|
||||
expr2,
|
||||
closure_data_layout.clone(),
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
|
||||
stmt = Stmt::Let(
|
||||
tag_id_symbol,
|
||||
expr1,
|
||||
Layout::Builtin(Builtin::Int64),
|
||||
env.arena.alloc(stmt),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5630,19 +5734,93 @@ where
|
|||
}
|
||||
|
||||
fn call_by_pointer<'a>(
|
||||
_env: &mut Env<'a, '_>,
|
||||
env: &mut Env<'a, '_>,
|
||||
procs: &mut Procs<'a>,
|
||||
symbol: Symbol,
|
||||
layout: Layout<'a>,
|
||||
) -> Expr<'a> {
|
||||
// let other = env.unique_symbol();
|
||||
let other = symbol;
|
||||
// when we call a known function by-pointer, we must make sure we call a function that owns all
|
||||
// its arguments (in an RC sense). we can't know this at this point, so we wrap such calls in
|
||||
// a proc that we guarantee owns all its arguments. E.g. we turn
|
||||
//
|
||||
// foo = \x -> ...
|
||||
//
|
||||
// x = List.map [ ... ] foo
|
||||
//
|
||||
// into
|
||||
//
|
||||
// foo = \x -> ...
|
||||
//
|
||||
// @owns_all_arguments
|
||||
// foo1 = \x -> foo x
|
||||
//
|
||||
// x = List.map [ ... ] foo1
|
||||
|
||||
// TODO can we cache this `any`?
|
||||
let is_specialized = procs.specialized.keys().any(|(s, _)| *s == symbol);
|
||||
if env.is_imported_symbol(symbol) || procs.partial_procs.contains_key(&symbol) || is_specialized
|
||||
{
|
||||
match layout {
|
||||
Layout::FunctionPointer(arg_layouts, ret_layout) => {
|
||||
if arg_layouts.iter().any(|l| l.contains_refcounted()) {
|
||||
let name = env.unique_symbol();
|
||||
let mut args = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
||||
let mut arg_symbols = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
||||
|
||||
for layout in arg_layouts {
|
||||
let symbol = env.unique_symbol();
|
||||
args.push((layout.clone(), symbol));
|
||||
arg_symbols.push(symbol);
|
||||
}
|
||||
let args = args.into_bump_slice();
|
||||
|
||||
let call_symbol = env.unique_symbol();
|
||||
let call_type = CallType::ByName {
|
||||
name: symbol,
|
||||
full_layout: layout.clone(),
|
||||
ret_layout: ret_layout.clone(),
|
||||
arg_layouts,
|
||||
};
|
||||
let call = Call {
|
||||
call_type,
|
||||
arguments: arg_symbols.into_bump_slice(),
|
||||
};
|
||||
let expr = Expr::Call(call);
|
||||
|
||||
let mut body = Stmt::Ret(call_symbol);
|
||||
|
||||
body = Stmt::Let(call_symbol, expr, ret_layout.clone(), env.arena.alloc(body));
|
||||
|
||||
let closure_data_layout = None;
|
||||
let proc = Proc {
|
||||
name,
|
||||
args,
|
||||
body,
|
||||
closure_data_layout,
|
||||
ret_layout: ret_layout.clone(),
|
||||
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||
must_own_arguments: true,
|
||||
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||
};
|
||||
|
||||
procs
|
||||
.passed_by_pointer
|
||||
.insert((symbol, layout.clone()), other);
|
||||
|
||||
Expr::FunctionPointer(other, layout)
|
||||
.specialized
|
||||
.insert((name, layout.clone()), InProgressProc::Done(proc));
|
||||
Expr::FunctionPointer(name, layout)
|
||||
} else {
|
||||
// if none of the arguments is refcounted, then owning the arguments has no
|
||||
// meaning
|
||||
Expr::FunctionPointer(symbol, layout)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// e.g. Num.maxInt or other constants
|
||||
Expr::FunctionPointer(symbol, layout)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Expr::FunctionPointer(symbol, layout)
|
||||
}
|
||||
}
|
||||
|
||||
fn add_needed_external<'a>(
|
||||
|
|
|
@ -79,7 +79,7 @@ impl<'a> UnionLayout<'a> {
|
|||
let tags_doc = tags.iter().map(|fields| {
|
||||
alloc.text("C ").append(alloc.intersperse(
|
||||
fields.iter().map(|x| x.to_doc(alloc, Parens::InTypeParam)),
|
||||
", ",
|
||||
" ",
|
||||
))
|
||||
});
|
||||
|
||||
|
@ -316,16 +316,16 @@ impl<'a> ClosureLayout<'a> {
|
|||
&self,
|
||||
original: Symbol,
|
||||
symbols: &'a [Symbol],
|
||||
) -> Result<crate::ir::Expr<'a>, Symbol> {
|
||||
) -> BuildClosureData<'a> {
|
||||
use crate::ir::Expr;
|
||||
|
||||
match self.layout {
|
||||
Layout::Struct(fields) if fields.len() == 1 => Err(symbols[0]),
|
||||
Layout::Struct(fields) if fields.len() == 1 => BuildClosureData::Alias(symbols[0]),
|
||||
Layout::Struct(fields) => {
|
||||
debug_assert!(fields.len() > 1);
|
||||
debug_assert_eq!(fields.len(), symbols.len());
|
||||
|
||||
Ok(Expr::Struct(symbols))
|
||||
BuildClosureData::Struct(Expr::Struct(symbols))
|
||||
}
|
||||
Layout::Union(UnionLayout::NonRecursive(tags)) => {
|
||||
// NOTE it's very important that this Union consists of Closure tags
|
||||
|
@ -337,20 +337,17 @@ impl<'a> ClosureLayout<'a> {
|
|||
.position(|(tn, _)| *tn == TagName::Closure(original))
|
||||
.unwrap() as _;
|
||||
|
||||
let expr = Expr::Tag {
|
||||
BuildClosureData::Union {
|
||||
tag_layout: Layout::Union(UnionLayout::NonRecursive(tags)),
|
||||
tag_name: TagName::Closure(original),
|
||||
tag_id,
|
||||
union_size: tags.len() as u8,
|
||||
arguments: symbols,
|
||||
};
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
}
|
||||
Layout::PhantomEmptyStruct => {
|
||||
debug_assert_eq!(symbols.len(), 1);
|
||||
|
||||
Ok(Expr::Struct(&[]))
|
||||
BuildClosureData::Struct(Expr::Struct(&[]))
|
||||
}
|
||||
|
||||
_ => {
|
||||
|
@ -362,12 +359,23 @@ impl<'a> ClosureLayout<'a> {
|
|||
&self.layout
|
||||
);
|
||||
|
||||
Err(symbols[0])
|
||||
BuildClosureData::Alias(symbols[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub enum BuildClosureData<'a> {
|
||||
Alias(Symbol),
|
||||
Struct(crate::ir::Expr<'a>),
|
||||
Union {
|
||||
tag_layout: Layout<'a>,
|
||||
tag_name: TagName,
|
||||
tag_id: u8,
|
||||
union_size: u8,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Copy)]
|
||||
pub enum MemoryMode {
|
||||
Unique,
|
||||
|
|
|
@ -1969,15 +1969,14 @@ mod test_mono {
|
|||
let Test.7 = Index 1 Test.2;
|
||||
let Test.8 = 0i64;
|
||||
let Test.9 = Index 0 Test.7;
|
||||
dec Test.7;
|
||||
decref Test.2;
|
||||
let Test.10 = lowlevel Eq Test.8 Test.9;
|
||||
if Test.10 then
|
||||
let Test.4 = Index 1 Test.2;
|
||||
let Test.3 = 1i64;
|
||||
decref Test.2;
|
||||
ret Test.3;
|
||||
else
|
||||
let Test.5 = 0i64;
|
||||
dec Test.2;
|
||||
ret Test.5;
|
||||
else
|
||||
let Test.6 = 0i64;
|
||||
|
|
|
@ -12,4 +12,6 @@ main =
|
|||
|
||||
Task.succeed {}
|
||||
|> Task.map (\_ -> x)
|
||||
|> Task.map (\_ -> {})
|
||||
|> Task.map toUnit
|
||||
|
||||
toUnit = (\_ -> {})
|
||||
|
|
|
@ -12,4 +12,3 @@ main =
|
|||
|
||||
Task.succeed {}
|
||||
|> Task.after (\_ -> Task.succeed x |> Task.map (\_ -> {}))
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue