mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-30 15:21:12 +00:00
Merge branch 'trunk' into rc-missed-opt
This commit is contained in:
commit
cebf5db894
6 changed files with 256 additions and 199 deletions
|
@ -3773,12 +3773,7 @@ fn make_specializations<'a>(
|
||||||
// TODO: for now this final specialization pass is sequential,
|
// TODO: for now this final specialization pass is sequential,
|
||||||
// with no parallelization at all. We should try to parallelize
|
// with no parallelization at all. We should try to parallelize
|
||||||
// this, but doing so will require a redesign of Procs.
|
// this, but doing so will require a redesign of Procs.
|
||||||
procs = roc_mono::ir::specialize_all(
|
procs = roc_mono::ir::specialize_all(&mut mono_env, procs, &mut layout_cache);
|
||||||
&mut mono_env,
|
|
||||||
procs,
|
|
||||||
&mut layout_cache,
|
|
||||||
// &finished_info.vars_by_symbol,
|
|
||||||
);
|
|
||||||
|
|
||||||
let external_specializations_requested = procs.externals_we_need.clone();
|
let external_specializations_requested = procs.externals_we_need.clone();
|
||||||
let procedures = procs.get_specialized_procs_without_rc(mono_env.arena);
|
let procedures = procs.get_specialized_procs_without_rc(mono_env.arena);
|
||||||
|
|
|
@ -558,13 +558,7 @@ impl<'a> Procs<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error_msg = format!(
|
panic!("TODO generate a RuntimeError message for {:?}", error);
|
||||||
"TODO generate a RuntimeError message for {:?}",
|
|
||||||
error
|
|
||||||
);
|
|
||||||
self.runtime_errors
|
|
||||||
.insert(symbol, env.arena.alloc(error_msg));
|
|
||||||
panic!();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -671,11 +665,7 @@ impl<'a> Procs<'a> {
|
||||||
self.specialized.insert((symbol, layout), Done(proc));
|
self.specialized.insert((symbol, layout), Done(proc));
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
let error_msg =
|
panic!("TODO generate a RuntimeError message for {:?}", error);
|
||||||
format!("TODO generate a RuntimeError message for {:?}", error);
|
|
||||||
self.runtime_errors
|
|
||||||
.insert(symbol, env.arena.alloc(error_msg));
|
|
||||||
panic!();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -699,7 +689,6 @@ fn add_pending<'a>(
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct Specializations<'a> {
|
pub struct Specializations<'a> {
|
||||||
by_symbol: MutMap<Symbol, MutMap<Layout<'a>, Proc<'a>>>,
|
by_symbol: MutMap<Symbol, MutMap<Layout<'a>, Proc<'a>>>,
|
||||||
runtime_errors: MutSet<Symbol>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Specializations<'a> {
|
impl<'a> Specializations<'a> {
|
||||||
|
@ -715,32 +704,15 @@ impl<'a> Specializations<'a> {
|
||||||
!procs_by_layout.contains_key(&layout) || procs_by_layout.get(&layout) == Some(&proc)
|
!procs_by_layout.contains_key(&layout) || procs_by_layout.get(&layout) == Some(&proc)
|
||||||
);
|
);
|
||||||
|
|
||||||
// We shouldn't already have a runtime error recorded for this symbol
|
|
||||||
debug_assert!(!self.runtime_errors.contains(&symbol));
|
|
||||||
|
|
||||||
procs_by_layout.insert(layout, proc);
|
procs_by_layout.insert(layout, proc);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runtime_error(&mut self, symbol: Symbol) {
|
|
||||||
// We shouldn't already have a normal proc recorded for this symbol
|
|
||||||
debug_assert!(!self.by_symbol.contains_key(&symbol));
|
|
||||||
|
|
||||||
self.runtime_errors.insert(symbol);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_owned(self) -> (MutMap<Symbol, MutMap<Layout<'a>, Proc<'a>>>, MutSet<Symbol>) {
|
|
||||||
(self.by_symbol, self.runtime_errors)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
let runtime_errors: usize = self.runtime_errors.len();
|
self.by_symbol.len()
|
||||||
let specializations: usize = self.by_symbol.len();
|
|
||||||
|
|
||||||
runtime_errors + specializations
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
self.len() == 0
|
self.by_symbol.is_empty()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1696,13 +1668,15 @@ pub fn specialize_all<'a>(
|
||||||
Ok((proc, layout)) => {
|
Ok((proc, layout)) => {
|
||||||
procs.specialized.insert((name, layout), Done(proc));
|
procs.specialized.insert((name, layout), Done(proc));
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(SpecializeFailure {
|
||||||
let error_msg = env.arena.alloc(format!(
|
problem: _,
|
||||||
"TODO generate a RuntimeError message for {:?}",
|
attempted_layout,
|
||||||
error
|
}) => {
|
||||||
));
|
let proc = generate_runtime_error_function(env, name, attempted_layout);
|
||||||
|
|
||||||
procs.runtime_errors.insert(name, error_msg);
|
procs
|
||||||
|
.specialized
|
||||||
|
.insert((name, attempted_layout), Done(proc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1758,13 +1732,14 @@ pub fn specialize_all<'a>(
|
||||||
procs.specialized.insert((name, layout), Done(proc));
|
procs.specialized.insert((name, layout), Done(proc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(SpecializeFailure {
|
||||||
let error_msg = env.arena.alloc(format!(
|
attempted_layout, ..
|
||||||
"TODO generate a RuntimeError message for {:?}",
|
}) => {
|
||||||
error
|
let proc = generate_runtime_error_function(env, name, attempted_layout);
|
||||||
));
|
|
||||||
|
|
||||||
procs.runtime_errors.insert(name, error_msg);
|
procs
|
||||||
|
.specialized
|
||||||
|
.insert((name, attempted_layout), Done(proc));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1774,6 +1749,47 @@ pub fn specialize_all<'a>(
|
||||||
procs
|
procs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generate_runtime_error_function<'a>(
|
||||||
|
env: &mut Env<'a, '_>,
|
||||||
|
name: Symbol,
|
||||||
|
layout: Layout<'a>,
|
||||||
|
) -> Proc<'a> {
|
||||||
|
let (arg_layouts, ret_layout) = match layout {
|
||||||
|
Layout::FunctionPointer(a, r) => (a, *r),
|
||||||
|
_ => (&[] as &[_], layout),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut args = Vec::with_capacity_in(arg_layouts.len(), env.arena);
|
||||||
|
|
||||||
|
for arg in arg_layouts {
|
||||||
|
args.push((*arg, env.unique_symbol()));
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut msg = bumpalo::collections::string::String::with_capacity_in(80, env.arena);
|
||||||
|
use std::fmt::Write;
|
||||||
|
write!(
|
||||||
|
&mut msg,
|
||||||
|
"The {:?} function could not be generated, likely due to a type error.",
|
||||||
|
name
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
eprintln!("emitted runtime error function {:?}", &msg);
|
||||||
|
|
||||||
|
let runtime_error = Stmt::RuntimeError(msg.into_bump_str());
|
||||||
|
|
||||||
|
Proc {
|
||||||
|
name,
|
||||||
|
args: args.into_bump_slice(),
|
||||||
|
body: runtime_error,
|
||||||
|
closure_data_layout: None,
|
||||||
|
ret_layout,
|
||||||
|
is_self_recursive: SelfRecursive::NotSelfRecursive,
|
||||||
|
must_own_arguments: false,
|
||||||
|
host_exposed_layouts: HostExposedLayouts::NotHostExposed,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn specialize_external<'a>(
|
fn specialize_external<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
|
@ -2270,6 +2286,14 @@ fn build_specialized_proc<'a>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
struct SpecializeFailure<'a> {
|
||||||
|
/// The layout we attempted to create
|
||||||
|
attempted_layout: Layout<'a>,
|
||||||
|
/// The problem we ran into while creating it
|
||||||
|
problem: LayoutProblem,
|
||||||
|
}
|
||||||
|
|
||||||
fn specialize<'a>(
|
fn specialize<'a>(
|
||||||
env: &mut Env<'a, '_>,
|
env: &mut Env<'a, '_>,
|
||||||
procs: &mut Procs<'a>,
|
procs: &mut Procs<'a>,
|
||||||
|
@ -2277,7 +2301,7 @@ fn specialize<'a>(
|
||||||
layout_cache: &mut LayoutCache<'a>,
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
pending: PendingSpecialization,
|
pending: PendingSpecialization,
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc: PartialProc<'a>,
|
||||||
) -> Result<(Proc<'a>, Layout<'a>), LayoutProblem> {
|
) -> Result<(Proc<'a>, Layout<'a>), SpecializeFailure<'a>> {
|
||||||
let PendingSpecialization {
|
let PendingSpecialization {
|
||||||
solved_type,
|
solved_type,
|
||||||
host_exposed_aliases,
|
host_exposed_aliases,
|
||||||
|
@ -2321,7 +2345,7 @@ fn specialize_solved_type<'a>(
|
||||||
solved_type: SolvedType,
|
solved_type: SolvedType,
|
||||||
host_exposed_aliases: MutMap<Symbol, SolvedType>,
|
host_exposed_aliases: MutMap<Symbol, SolvedType>,
|
||||||
partial_proc: PartialProc<'a>,
|
partial_proc: PartialProc<'a>,
|
||||||
) -> Result<(Proc<'a>, Layout<'a>), LayoutProblem> {
|
) -> Result<(Proc<'a>, Layout<'a>), SpecializeFailure<'a>> {
|
||||||
// add the specializations that other modules require of us
|
// add the specializations that other modules require of us
|
||||||
use roc_solve::solve::instantiate_rigids;
|
use roc_solve::solve::instantiate_rigids;
|
||||||
|
|
||||||
|
@ -2330,6 +2354,10 @@ fn specialize_solved_type<'a>(
|
||||||
|
|
||||||
let fn_var = introduce_solved_type_to_subs(env, &solved_type);
|
let fn_var = introduce_solved_type_to_subs(env, &solved_type);
|
||||||
|
|
||||||
|
let attempted_layout = layout_cache
|
||||||
|
.from_var(&env.arena, fn_var, env.subs)
|
||||||
|
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
||||||
|
|
||||||
// make sure rigid variables in the annotation are converted to flex variables
|
// make sure rigid variables in the annotation are converted to flex variables
|
||||||
instantiate_rigids(env.subs, partial_proc.annotation);
|
instantiate_rigids(env.subs, partial_proc.annotation);
|
||||||
|
|
||||||
|
@ -2353,17 +2381,25 @@ fn specialize_solved_type<'a>(
|
||||||
|
|
||||||
match specialized {
|
match specialized {
|
||||||
Ok(proc) => {
|
Ok(proc) => {
|
||||||
let layout = layout_cache
|
// when successful, the layout after unification should be the layout before unification
|
||||||
.from_var(&env.arena, fn_var, env.subs)
|
debug_assert_eq!(
|
||||||
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err));
|
attempted_layout,
|
||||||
|
layout_cache
|
||||||
|
.from_var(&env.arena, fn_var, env.subs)
|
||||||
|
.unwrap_or_else(|err| panic!("TODO handle invalid function {:?}", err))
|
||||||
|
);
|
||||||
|
|
||||||
env.subs.rollback_to(snapshot);
|
env.subs.rollback_to(snapshot);
|
||||||
layout_cache.rollback_to(cache_snapshot);
|
layout_cache.rollback_to(cache_snapshot);
|
||||||
Ok((proc, layout))
|
Ok((proc, attempted_layout))
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(error) => {
|
||||||
env.subs.rollback_to(snapshot);
|
env.subs.rollback_to(snapshot);
|
||||||
layout_cache.rollback_to(cache_snapshot);
|
layout_cache.rollback_to(cache_snapshot);
|
||||||
Err(error)
|
Err(SpecializeFailure {
|
||||||
|
problem: error,
|
||||||
|
attempted_layout,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -5893,6 +5929,20 @@ fn call_by_name<'a>(
|
||||||
|
|
||||||
// Register a pending_specialization for this function
|
// Register a pending_specialization for this function
|
||||||
match layout_cache.from_var(env.arena, fn_var, env.subs) {
|
match layout_cache.from_var(env.arena, fn_var, env.subs) {
|
||||||
|
Err(LayoutProblem::UnresolvedTypeVar(var)) => {
|
||||||
|
let msg = format!(
|
||||||
|
"Hit an unresolved type variable {:?} when creating a layout for {:?} (var {:?})",
|
||||||
|
var, proc_name, fn_var
|
||||||
|
);
|
||||||
|
Stmt::RuntimeError(env.arena.alloc(msg))
|
||||||
|
}
|
||||||
|
Err(LayoutProblem::Erroneous) => {
|
||||||
|
let msg = format!(
|
||||||
|
"Hit an erroneous type when creating a layout for {:?}",
|
||||||
|
proc_name
|
||||||
|
);
|
||||||
|
Stmt::RuntimeError(env.arena.alloc(msg))
|
||||||
|
}
|
||||||
Ok(layout) => {
|
Ok(layout) => {
|
||||||
// Build the CallByName node
|
// Build the CallByName node
|
||||||
let arena = env.arena;
|
let arena = env.arena;
|
||||||
|
@ -6028,123 +6078,42 @@ fn call_by_name<'a>(
|
||||||
"\n\n{:?}\n\n{:?}",
|
"\n\n{:?}\n\n{:?}",
|
||||||
full_layout, layout
|
full_layout, layout
|
||||||
);
|
);
|
||||||
let function_layout =
|
|
||||||
FunctionLayouts::from_layout(env.arena, layout);
|
|
||||||
|
|
||||||
procs.specialized.remove(&(proc_name, full_layout));
|
call_specialized_proc(
|
||||||
|
env,
|
||||||
procs
|
procs,
|
||||||
.specialized
|
proc_name,
|
||||||
.insert((proc_name, function_layout.full), Done(proc));
|
proc,
|
||||||
|
layout,
|
||||||
if field_symbols.is_empty() {
|
field_symbols,
|
||||||
debug_assert!(loc_args.is_empty());
|
loc_args,
|
||||||
|
layout_cache,
|
||||||
// This happens when we return a function, e.g.
|
assigned,
|
||||||
//
|
hole,
|
||||||
// foo = Num.add
|
)
|
||||||
//
|
|
||||||
// Even though the layout (and type) are functions,
|
|
||||||
// there are no arguments. This confuses our IR,
|
|
||||||
// and we have to fix it here.
|
|
||||||
match full_layout {
|
|
||||||
Layout::Closure(_, closure_layout, _) => {
|
|
||||||
let call = self::Call {
|
|
||||||
call_type: CallType::ByName {
|
|
||||||
name: proc_name,
|
|
||||||
ret_layout: function_layout.result,
|
|
||||||
full_layout: function_layout.full,
|
|
||||||
arg_layouts: function_layout.arguments,
|
|
||||||
},
|
|
||||||
arguments: field_symbols,
|
|
||||||
};
|
|
||||||
|
|
||||||
// in the case of a closure specifically, we
|
|
||||||
// have to create a custom layout, to make sure
|
|
||||||
// the closure data is part of the layout
|
|
||||||
let closure_struct_layout = Layout::Struct(
|
|
||||||
env.arena.alloc([
|
|
||||||
function_layout.full,
|
|
||||||
closure_layout
|
|
||||||
.as_block_of_memory_layout(),
|
|
||||||
]),
|
|
||||||
);
|
|
||||||
|
|
||||||
build_call(
|
|
||||||
env,
|
|
||||||
call,
|
|
||||||
assigned,
|
|
||||||
closure_struct_layout,
|
|
||||||
hole,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
let call = self::Call {
|
|
||||||
call_type: CallType::ByName {
|
|
||||||
name: proc_name,
|
|
||||||
ret_layout: function_layout.result,
|
|
||||||
full_layout: function_layout.full,
|
|
||||||
arg_layouts: function_layout.arguments,
|
|
||||||
},
|
|
||||||
arguments: field_symbols,
|
|
||||||
};
|
|
||||||
|
|
||||||
build_call(
|
|
||||||
env,
|
|
||||||
call,
|
|
||||||
assigned,
|
|
||||||
function_layout.full,
|
|
||||||
hole,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
debug_assert_eq!(
|
|
||||||
function_layout.arguments.len(),
|
|
||||||
field_symbols.len(),
|
|
||||||
"scroll up a bit for background"
|
|
||||||
);
|
|
||||||
let call = self::Call {
|
|
||||||
call_type: CallType::ByName {
|
|
||||||
name: proc_name,
|
|
||||||
ret_layout: function_layout.result,
|
|
||||||
full_layout: function_layout.full,
|
|
||||||
arg_layouts: function_layout.arguments,
|
|
||||||
},
|
|
||||||
arguments: field_symbols,
|
|
||||||
};
|
|
||||||
|
|
||||||
let iter = loc_args
|
|
||||||
.into_iter()
|
|
||||||
.rev()
|
|
||||||
.zip(field_symbols.iter().rev());
|
|
||||||
|
|
||||||
let result = build_call(
|
|
||||||
env,
|
|
||||||
call,
|
|
||||||
assigned,
|
|
||||||
function_layout.result,
|
|
||||||
hole,
|
|
||||||
);
|
|
||||||
|
|
||||||
assign_to_symbols(
|
|
||||||
env,
|
|
||||||
procs,
|
|
||||||
layout_cache,
|
|
||||||
iter,
|
|
||||||
result,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Err(error) => {
|
Err(SpecializeFailure {
|
||||||
let error_msg = env.arena.alloc(format!(
|
attempted_layout,
|
||||||
"TODO generate a RuntimeError message for {:?}",
|
problem: _,
|
||||||
error
|
}) => {
|
||||||
));
|
let proc = generate_runtime_error_function(
|
||||||
|
env,
|
||||||
|
proc_name,
|
||||||
|
attempted_layout,
|
||||||
|
);
|
||||||
|
|
||||||
procs.runtime_errors.insert(proc_name, error_msg);
|
call_specialized_proc(
|
||||||
|
env,
|
||||||
Stmt::RuntimeError(error_msg)
|
procs,
|
||||||
|
proc_name,
|
||||||
|
proc,
|
||||||
|
layout,
|
||||||
|
field_symbols,
|
||||||
|
loc_args,
|
||||||
|
layout_cache,
|
||||||
|
assigned,
|
||||||
|
hole,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6191,33 +6160,104 @@ fn call_by_name<'a>(
|
||||||
}
|
}
|
||||||
|
|
||||||
None => {
|
None => {
|
||||||
// This must have been a runtime error.
|
unreachable!("Proc name {:?} is invalid", proc_name)
|
||||||
match procs.runtime_errors.get(&proc_name) {
|
|
||||||
Some(error) => Stmt::RuntimeError(
|
|
||||||
env.arena.alloc(format!("runtime error {:?}", error)),
|
|
||||||
),
|
|
||||||
None => unreachable!("Proc name {:?} is invalid", proc_name),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(LayoutProblem::UnresolvedTypeVar(var)) => {
|
}
|
||||||
let msg = format!(
|
}
|
||||||
"Hit an unresolved type variable {:?} when creating a layout for {:?} (var {:?})",
|
|
||||||
var, proc_name, fn_var
|
#[allow(clippy::too_many_arguments)]
|
||||||
);
|
fn call_specialized_proc<'a>(
|
||||||
Stmt::RuntimeError(env.arena.alloc(msg))
|
env: &mut Env<'a, '_>,
|
||||||
}
|
procs: &mut Procs<'a>,
|
||||||
Err(LayoutProblem::Erroneous) => {
|
proc_name: Symbol,
|
||||||
let msg = format!(
|
proc: Proc<'a>,
|
||||||
"Hit an erroneous type when creating a layout for {:?}",
|
layout: Layout<'a>,
|
||||||
proc_name
|
field_symbols: &'a [Symbol],
|
||||||
);
|
loc_args: std::vec::Vec<(Variable, Located<roc_can::expr::Expr>)>,
|
||||||
Stmt::RuntimeError(env.arena.alloc(msg))
|
layout_cache: &mut LayoutCache<'a>,
|
||||||
|
assigned: Symbol,
|
||||||
|
hole: &'a Stmt<'a>,
|
||||||
|
) -> Stmt<'a> {
|
||||||
|
let function_layout = FunctionLayouts::from_layout(env.arena, layout);
|
||||||
|
|
||||||
|
procs.specialized.remove(&(proc_name, layout));
|
||||||
|
|
||||||
|
procs
|
||||||
|
.specialized
|
||||||
|
.insert((proc_name, function_layout.full), Done(proc));
|
||||||
|
|
||||||
|
if field_symbols.is_empty() {
|
||||||
|
debug_assert!(loc_args.is_empty());
|
||||||
|
|
||||||
|
// This happens when we return a function, e.g.
|
||||||
|
//
|
||||||
|
// foo = Num.add
|
||||||
|
//
|
||||||
|
// Even though the layout (and type) are functions,
|
||||||
|
// there are no arguments. This confuses our IR,
|
||||||
|
// and we have to fix it here.
|
||||||
|
match layout {
|
||||||
|
Layout::Closure(_, closure_layout, _) => {
|
||||||
|
let call = self::Call {
|
||||||
|
call_type: CallType::ByName {
|
||||||
|
name: proc_name,
|
||||||
|
ret_layout: function_layout.result,
|
||||||
|
full_layout: function_layout.full,
|
||||||
|
arg_layouts: function_layout.arguments,
|
||||||
|
},
|
||||||
|
arguments: field_symbols,
|
||||||
|
};
|
||||||
|
|
||||||
|
// in the case of a closure specifically, we
|
||||||
|
// have to create a custom layout, to make sure
|
||||||
|
// the closure data is part of the layout
|
||||||
|
let closure_struct_layout = Layout::Struct(env.arena.alloc([
|
||||||
|
function_layout.full,
|
||||||
|
closure_layout.as_block_of_memory_layout(),
|
||||||
|
]));
|
||||||
|
|
||||||
|
build_call(env, call, assigned, closure_struct_layout, hole)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let call = self::Call {
|
||||||
|
call_type: CallType::ByName {
|
||||||
|
name: proc_name,
|
||||||
|
ret_layout: function_layout.result,
|
||||||
|
full_layout: function_layout.full,
|
||||||
|
arg_layouts: function_layout.arguments,
|
||||||
|
},
|
||||||
|
arguments: field_symbols,
|
||||||
|
};
|
||||||
|
|
||||||
|
build_call(env, call, assigned, function_layout.full, hole)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
debug_assert_eq!(
|
||||||
|
function_layout.arguments.len(),
|
||||||
|
field_symbols.len(),
|
||||||
|
"scroll up a bit for background"
|
||||||
|
);
|
||||||
|
let call = self::Call {
|
||||||
|
call_type: CallType::ByName {
|
||||||
|
name: proc_name,
|
||||||
|
ret_layout: function_layout.result,
|
||||||
|
full_layout: function_layout.full,
|
||||||
|
arg_layouts: function_layout.arguments,
|
||||||
|
},
|
||||||
|
arguments: field_symbols,
|
||||||
|
};
|
||||||
|
|
||||||
|
let iter = loc_args.into_iter().rev().zip(field_symbols.iter().rev());
|
||||||
|
|
||||||
|
let result = build_call(env, call, assigned, function_layout.result, hole);
|
||||||
|
|
||||||
|
assign_to_symbols(env, procs, layout_cache, iter, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -341,13 +341,13 @@ mod test_mono {
|
||||||
"#,
|
"#,
|
||||||
indoc!(
|
indoc!(
|
||||||
r#"
|
r#"
|
||||||
procedure Num.46 (#Attr.2):
|
procedure Num.47 (#Attr.2):
|
||||||
let Test.3 = lowlevel NumRound #Attr.2;
|
let Test.3 = lowlevel NumRound #Attr.2;
|
||||||
ret Test.3;
|
ret Test.3;
|
||||||
|
|
||||||
procedure Test.0 ():
|
procedure Test.0 ():
|
||||||
let Test.2 = 3.6f64;
|
let Test.2 = 3.6f64;
|
||||||
let Test.1 = CallByName Num.46 Test.2;
|
let Test.1 = CallByName Num.47 Test.2;
|
||||||
ret Test.1;
|
ret Test.1;
|
||||||
"#
|
"#
|
||||||
),
|
),
|
||||||
|
|
|
@ -404,6 +404,7 @@ mod gen_num {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
fn f64_sqrt_zero() {
|
fn f64_sqrt_zero() {
|
||||||
assert_evals_to!(
|
assert_evals_to!(
|
||||||
indoc!(
|
indoc!(
|
||||||
|
|
|
@ -2276,3 +2276,23 @@ fn function_malformed_pattern() {
|
||||||
i64
|
i64
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = "Hit an erroneous type when creating a layout for")]
|
||||||
|
fn call_invalid_layout() {
|
||||||
|
assert_llvm_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
f : I64 -> I64
|
||||||
|
f = \x -> x
|
||||||
|
|
||||||
|
f {}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
3,
|
||||||
|
i64,
|
||||||
|
|x| x,
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub fn helper<'a>(
|
||||||
src: &str,
|
src: &str,
|
||||||
stdlib: &'a roc_builtins::std::StdLib,
|
stdlib: &'a roc_builtins::std::StdLib,
|
||||||
leak: bool,
|
leak: bool,
|
||||||
|
ignore_problems: bool,
|
||||||
context: &'a inkwell::context::Context,
|
context: &'a inkwell::context::Context,
|
||||||
) -> (&'static str, String, Library) {
|
) -> (&'static str, String, Library) {
|
||||||
use roc_gen::llvm::build::{build_proc, build_proc_header, Scope};
|
use roc_gen::llvm::build::{build_proc, build_proc_header, Scope};
|
||||||
|
@ -170,7 +171,7 @@ pub fn helper<'a>(
|
||||||
println!("{}", lines.join("\n"));
|
println!("{}", lines.join("\n"));
|
||||||
|
|
||||||
// only crash at this point if there were no delayed_errors
|
// only crash at this point if there were no delayed_errors
|
||||||
if delayed_errors.is_empty() {
|
if delayed_errors.is_empty() && !ignore_problems {
|
||||||
assert_eq!(0, 1, "Mistakes were made");
|
assert_eq!(0, 1, "Mistakes were made");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -331,7 +332,7 @@ pub fn helper<'a>(
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! assert_llvm_evals_to {
|
macro_rules! assert_llvm_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr, $ignore_problems:expr) => {
|
||||||
use bumpalo::Bump;
|
use bumpalo::Bump;
|
||||||
use inkwell::context::Context;
|
use inkwell::context::Context;
|
||||||
use roc_gen::run_jit_function;
|
use roc_gen::run_jit_function;
|
||||||
|
@ -343,7 +344,7 @@ macro_rules! assert_llvm_evals_to {
|
||||||
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
let stdlib = arena.alloc(roc_builtins::std::standard_stdlib());
|
||||||
|
|
||||||
let (main_fn_name, errors, lib) =
|
let (main_fn_name, errors, lib) =
|
||||||
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, &context);
|
$crate::helpers::eval::helper(&arena, $src, stdlib, $leak, $ignore_problems, &context);
|
||||||
|
|
||||||
let transform = |success| {
|
let transform = |success| {
|
||||||
let expected = $expected;
|
let expected = $expected;
|
||||||
|
@ -354,7 +355,7 @@ macro_rules! assert_llvm_evals_to {
|
||||||
};
|
};
|
||||||
|
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, true);
|
assert_llvm_evals_to!($src, $expected, $ty, $transform, true, false);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +376,7 @@ macro_rules! assert_evals_to {
|
||||||
// parsing the source, so that there's no chance their passing
|
// parsing the source, so that there's no chance their passing
|
||||||
// or failing depends on leftover state from the previous one.
|
// or failing depends on leftover state from the previous one.
|
||||||
{
|
{
|
||||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak);
|
assert_llvm_evals_to!($src, $expected, $ty, $transform, $leak, false);
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
// NOTE at the moment, the optimized tests do the same thing
|
// NOTE at the moment, the optimized tests do the same thing
|
||||||
|
@ -392,7 +393,7 @@ macro_rules! assert_non_opt_evals_to {
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
($src:expr, $expected:expr, $ty:ty, $transform:expr) => {
|
||||||
// Same as above, except with an additional transformation argument.
|
// Same as above, except with an additional transformation argument.
|
||||||
{
|
{
|
||||||
assert_llvm_evals_to!($src, $expected, $ty, $transform, true);
|
assert_llvm_evals_to!($src, $expected, $ty, $transform, true, false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {{
|
($src:expr, $expected:expr, $ty:ty, $transform:expr, $leak:expr) => {{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue