mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
mostly fix issues with patterns/guards/switch/cond
This commit is contained in:
parent
dab00f2e2d
commit
db0bed2fe7
7 changed files with 445 additions and 320 deletions
|
@ -301,7 +301,37 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
call_type: ByPointer(name),
|
||||
layout,
|
||||
args,
|
||||
} => todo!(),
|
||||
} => {
|
||||
let sub_expr = load_symbol(env, scope, name);
|
||||
|
||||
let mut arg_vals: Vec<BasicValueEnum> = Vec::with_capacity_in(args.len(), env.arena);
|
||||
|
||||
for arg in args.iter() {
|
||||
arg_vals.push(load_symbol(env, scope, arg));
|
||||
}
|
||||
|
||||
let call = match sub_expr {
|
||||
BasicValueEnum::PointerValue(ptr) => {
|
||||
env.builder.build_call(ptr, arg_vals.as_slice(), "tmp")
|
||||
}
|
||||
non_ptr => {
|
||||
panic!(
|
||||
"Tried to call by pointer, but encountered a non-pointer: {:?}",
|
||||
non_ptr
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// TODO FIXME this should not be hardcoded!
|
||||
// Need to look up what calling convention is the right one for that function.
|
||||
// If this is an external-facing function, it'll use the C calling convention.
|
||||
// If it's an internal-only function, it should (someday) use the fast calling conention.
|
||||
call.set_call_convention(C_CALL_CONV);
|
||||
|
||||
call.try_as_basic_value()
|
||||
.left()
|
||||
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."))
|
||||
}
|
||||
|
||||
Struct(sorted_fields) => {
|
||||
let ctx = env.context;
|
||||
|
@ -566,7 +596,19 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
|||
Array { elem_layout, elems } => {
|
||||
list_literal2(env, layout_ids, scope, parent, elem_layout, elems)
|
||||
}
|
||||
FunctionPointer(_, _) => todo!(),
|
||||
FunctionPointer(symbol, layout) => {
|
||||
let fn_name = layout_ids
|
||||
.get(*symbol, layout)
|
||||
.to_symbol_string(*symbol, &env.interns);
|
||||
let ptr = env
|
||||
.module
|
||||
.get_function(fn_name.as_str())
|
||||
.unwrap_or_else(|| panic!("Could not get pointer to unknown function {:?}", symbol))
|
||||
.as_global_value()
|
||||
.as_pointer_value();
|
||||
|
||||
BasicValueEnum::PointerValue(ptr)
|
||||
}
|
||||
RuntimeErrorFunction(_) => todo!(),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -736,155 +736,155 @@ mod gen_list {
|
|||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foobar2() {
|
||||
with_larger_debug_stack(|| {
|
||||
assert_evals_to_ir!(
|
||||
indoc!(
|
||||
r#"
|
||||
quicksort : List (Num a) -> List (Num a)
|
||||
quicksort = \list ->
|
||||
quicksortHelp list 0 (List.len list - 1)
|
||||
// #[test]
|
||||
// fn foobar2() {
|
||||
// with_larger_debug_stack(|| {
|
||||
// assert_evals_to_ir!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// quicksort : List (Num a) -> List (Num a)
|
||||
// quicksort = \list ->
|
||||
// quicksortHelp list 0 (List.len list - 1)
|
||||
//
|
||||
//
|
||||
// quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
// quicksortHelp = \list, low, high ->
|
||||
// if low < high then
|
||||
// when partition low high list is
|
||||
// Pair partitionIndex partitioned ->
|
||||
// partitioned
|
||||
// |> quicksortHelp low (partitionIndex - 1)
|
||||
// |> quicksortHelp (partitionIndex + 1) high
|
||||
// else
|
||||
// list
|
||||
//
|
||||
//
|
||||
// swap : Int, Int, List a -> List a
|
||||
// swap = \i, j, list ->
|
||||
// when Pair (List.get list i) (List.get list j) is
|
||||
// Pair (Ok atI) (Ok atJ) ->
|
||||
// list
|
||||
// |> List.set i atJ
|
||||
// |> List.set j atI
|
||||
//
|
||||
// _ ->
|
||||
// []
|
||||
//
|
||||
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
// partition = \low, high, initialList ->
|
||||
// when List.get initialList high is
|
||||
// Ok pivot ->
|
||||
// when partitionHelp (low - 1) low initialList high pivot is
|
||||
// Pair newI newList ->
|
||||
// Pair (newI + 1) (swap (newI + 1) high newList)
|
||||
//
|
||||
// Err _ ->
|
||||
// Pair (low - 1) initialList
|
||||
//
|
||||
//
|
||||
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
|
||||
// partitionHelp = \i, j, list, high, pivot ->
|
||||
// # if j < high then
|
||||
// if False then
|
||||
// when List.get list j is
|
||||
// Ok value ->
|
||||
// if value <= pivot then
|
||||
// partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
||||
// else
|
||||
// partitionHelp i (j + 1) list high pivot
|
||||
//
|
||||
// Err _ ->
|
||||
// Pair i list
|
||||
// else
|
||||
// Pair i list
|
||||
//
|
||||
//
|
||||
//
|
||||
// quicksort [ 7, 4, 21, 19 ]
|
||||
// "#
|
||||
// ),
|
||||
// &[19, 7, 4, 21],
|
||||
// &'static [i64],
|
||||
// |x| x,
|
||||
// true
|
||||
// );
|
||||
// })
|
||||
// }
|
||||
|
||||
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
partitioned
|
||||
|> quicksortHelp low (partitionIndex - 1)
|
||||
|> quicksortHelp (partitionIndex + 1) high
|
||||
else
|
||||
list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
list
|
||||
|> List.set i atJ
|
||||
|> List.set j atI
|
||||
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
when partitionHelp (low - 1) low initialList high pivot is
|
||||
Pair newI newList ->
|
||||
Pair (newI + 1) (swap (newI + 1) high newList)
|
||||
|
||||
Err _ ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
# if j < high then
|
||||
if False then
|
||||
when List.get list j is
|
||||
Ok value ->
|
||||
if value <= pivot then
|
||||
partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
||||
else
|
||||
partitionHelp i (j + 1) list high pivot
|
||||
|
||||
Err _ ->
|
||||
Pair i list
|
||||
else
|
||||
Pair i list
|
||||
|
||||
|
||||
|
||||
quicksort [ 7, 4, 21, 19 ]
|
||||
"#
|
||||
),
|
||||
&[19, 7, 4, 21],
|
||||
&'static [i64],
|
||||
|x| x,
|
||||
true
|
||||
);
|
||||
})
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn foobar() {
|
||||
with_larger_debug_stack(|| {
|
||||
assert_evals_to_ir!(
|
||||
indoc!(
|
||||
r#"
|
||||
quicksort : List (Num a) -> List (Num a)
|
||||
quicksort = \list ->
|
||||
quicksortHelp list 0 (List.len list - 1)
|
||||
|
||||
|
||||
quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
quicksortHelp = \list, low, high ->
|
||||
if low < high then
|
||||
when partition low high list is
|
||||
Pair partitionIndex partitioned ->
|
||||
partitioned
|
||||
|> quicksortHelp low (partitionIndex - 1)
|
||||
|> quicksortHelp (partitionIndex + 1) high
|
||||
else
|
||||
list
|
||||
|
||||
|
||||
swap : Int, Int, List a -> List a
|
||||
swap = \i, j, list ->
|
||||
when Pair (List.get list i) (List.get list j) is
|
||||
Pair (Ok atI) (Ok atJ) ->
|
||||
list
|
||||
|> List.set i atJ
|
||||
|> List.set j atI
|
||||
|
||||
_ ->
|
||||
[]
|
||||
|
||||
partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
partition = \low, high, initialList ->
|
||||
when List.get initialList high is
|
||||
Ok pivot ->
|
||||
when partitionHelp (low - 1) low initialList high pivot is
|
||||
Pair newI newList ->
|
||||
Pair (newI + 1) (swap (newI + 1) high newList)
|
||||
|
||||
Err _ ->
|
||||
Pair (low - 1) initialList
|
||||
|
||||
|
||||
partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
|
||||
partitionHelp = \i, j, list, high, pivot ->
|
||||
if j < high then
|
||||
when List.get list j is
|
||||
Ok value ->
|
||||
if value <= pivot then
|
||||
partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
||||
else
|
||||
partitionHelp i (j + 1) list high pivot
|
||||
|
||||
Err _ ->
|
||||
Pair i list
|
||||
else
|
||||
Pair i list
|
||||
|
||||
|
||||
|
||||
when List.first (quicksort [0x1]) is
|
||||
_ -> 4
|
||||
"#
|
||||
),
|
||||
4,
|
||||
i64,
|
||||
|x| x,
|
||||
false
|
||||
);
|
||||
})
|
||||
}
|
||||
// #[test]
|
||||
// fn foobar() {
|
||||
// with_larger_debug_stack(|| {
|
||||
// assert_evals_to_ir!(
|
||||
// indoc!(
|
||||
// r#"
|
||||
// quicksort : List (Num a) -> List (Num a)
|
||||
// quicksort = \list ->
|
||||
// quicksortHelp list 0 (List.len list - 1)
|
||||
//
|
||||
//
|
||||
// quicksortHelp : List (Num a), Int, Int -> List (Num a)
|
||||
// quicksortHelp = \list, low, high ->
|
||||
// if low < high then
|
||||
// when partition low high list is
|
||||
// Pair partitionIndex partitioned ->
|
||||
// partitioned
|
||||
// |> quicksortHelp low (partitionIndex - 1)
|
||||
// |> quicksortHelp (partitionIndex + 1) high
|
||||
// else
|
||||
// list
|
||||
//
|
||||
//
|
||||
// swap : Int, Int, List a -> List a
|
||||
// swap = \i, j, list ->
|
||||
// when Pair (List.get list i) (List.get list j) is
|
||||
// Pair (Ok atI) (Ok atJ) ->
|
||||
// list
|
||||
// |> List.set i atJ
|
||||
// |> List.set j atI
|
||||
//
|
||||
// _ ->
|
||||
// []
|
||||
//
|
||||
// partition : Int, Int, List (Num a) -> [ Pair Int (List (Num a)) ]
|
||||
// partition = \low, high, initialList ->
|
||||
// when List.get initialList high is
|
||||
// Ok pivot ->
|
||||
// when partitionHelp (low - 1) low initialList high pivot is
|
||||
// Pair newI newList ->
|
||||
// Pair (newI + 1) (swap (newI + 1) high newList)
|
||||
//
|
||||
// Err _ ->
|
||||
// Pair (low - 1) initialList
|
||||
//
|
||||
//
|
||||
// partitionHelp : Int, Int, List (Num a), Int, Int -> [ Pair Int (List (Num a)) ]
|
||||
// partitionHelp = \i, j, list, high, pivot ->
|
||||
// if j < high then
|
||||
// when List.get list j is
|
||||
// Ok value ->
|
||||
// if value <= pivot then
|
||||
// partitionHelp (i + 1) (j + 1) (swap (i + 1) j list) high pivot
|
||||
// else
|
||||
// partitionHelp i (j + 1) list high pivot
|
||||
//
|
||||
// Err _ ->
|
||||
// Pair i list
|
||||
// else
|
||||
// Pair i list
|
||||
//
|
||||
//
|
||||
//
|
||||
// when List.first (quicksort [0x1]) is
|
||||
// _ -> 4
|
||||
// "#
|
||||
// ),
|
||||
// 4,
|
||||
// i64,
|
||||
// |x| x,
|
||||
// false
|
||||
// );
|
||||
// })
|
||||
// }
|
||||
|
||||
#[test]
|
||||
fn empty_list_increment_decrement() {
|
||||
|
@ -958,7 +958,7 @@ mod gen_list {
|
|||
assert_evals_to_ir!(
|
||||
indoc!(
|
||||
r#"
|
||||
id : List Int -> [ Pair (List Int) Int, Nil ]
|
||||
id : List Int -> [ Pair (List Int) Int ]
|
||||
id = \y -> Pair y 4
|
||||
|
||||
when id [1,2,3] is
|
||||
|
|
|
@ -68,6 +68,26 @@ mod gen_num {
|
|||
|
||||
#[test]
|
||||
fn gen_if_fn() {
|
||||
assert_evals_to_ir!(
|
||||
indoc!(
|
||||
r#"
|
||||
limitedNegate = \num ->
|
||||
x =
|
||||
if num == 1 then
|
||||
-1
|
||||
else if num == -1 then
|
||||
1
|
||||
else
|
||||
num
|
||||
x
|
||||
|
||||
limitedNegate 1
|
||||
"#
|
||||
),
|
||||
-1,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to_ir!(
|
||||
indoc!(
|
||||
r#"
|
||||
|
|
|
@ -391,4 +391,20 @@ mod gen_records {
|
|||
bool
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn return_record() {
|
||||
assert_evals_to_ir!(
|
||||
indoc!(
|
||||
r#"
|
||||
x = 4
|
||||
y = 3
|
||||
|
||||
{ x, y }
|
||||
"#
|
||||
),
|
||||
(4, 3),
|
||||
(i64, i64)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -585,6 +585,8 @@ fn to_relevant_branch_help<'a>(
|
|||
start.push((Path::Unbox(Box::new(path.clone())), guard, arg.0));
|
||||
start.extend(end);
|
||||
}
|
||||
} else if union.alternatives.len() == 1 {
|
||||
todo!("this should need a special index, right?")
|
||||
} else {
|
||||
let sub_positions =
|
||||
arguments
|
||||
|
@ -946,7 +948,7 @@ fn path_to_expr_help2<'a>(
|
|||
env: &mut Env<'a, '_>,
|
||||
mut symbol: Symbol,
|
||||
mut path: &Path,
|
||||
layout: Layout<'a>,
|
||||
mut layout: Layout<'a>,
|
||||
) -> (Symbol, StoresVec<'a>, Layout<'a>) {
|
||||
let mut stores = bumpalo::collections::Vec::new_in(env.arena);
|
||||
|
||||
|
@ -983,8 +985,9 @@ fn path_to_expr_help2<'a>(
|
|||
};
|
||||
|
||||
symbol = env.unique_symbol();
|
||||
stores.push((symbol, inner_layout, inner_expr));
|
||||
stores.push((symbol, inner_layout.clone(), inner_expr));
|
||||
|
||||
layout = inner_layout;
|
||||
path = nested;
|
||||
}
|
||||
}
|
||||
|
@ -1230,18 +1233,47 @@ fn decide_to_branching<'a>(
|
|||
}
|
||||
}
|
||||
|
||||
debug_assert!(!tests.is_empty());
|
||||
let mut current_symbol = branching_symbol;
|
||||
let mut condition_symbol = true_symbol;
|
||||
|
||||
// TODO There must be some way to remove this iterator/loop
|
||||
let nr = (tests.len() as i64) - 1 + (guard.is_some() as i64);
|
||||
let accum_symbols = std::iter::once(true_symbol)
|
||||
.chain((0..tests.len() - 1).map(|_| env.unique_symbol()))
|
||||
.chain((0..nr).map(|_| env.unique_symbol()))
|
||||
.rev()
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
for ((new_stores, lhs, rhs, layout), accum) in
|
||||
tests.into_iter().rev().zip(accum_symbols)
|
||||
{
|
||||
let mut accum_it = accum_symbols.into_iter();
|
||||
|
||||
// the guard is the final thing that we check, so needs to be layered on first!
|
||||
if let Some((_, id, stmt)) = guard {
|
||||
let accum = accum_it.next().unwrap();
|
||||
let test_symbol = env.unique_symbol();
|
||||
|
||||
let and_expr =
|
||||
Expr::RunLowLevel(LowLevel::And, env.arena.alloc([test_symbol, accum]));
|
||||
|
||||
// write to the branching symbol
|
||||
cond = Stmt::Let(
|
||||
current_symbol,
|
||||
and_expr,
|
||||
Layout::Builtin(Builtin::Int1),
|
||||
env.arena.alloc(cond),
|
||||
);
|
||||
|
||||
// calculate the guard value
|
||||
cond = Stmt::Join {
|
||||
id,
|
||||
arguments: env
|
||||
.arena
|
||||
.alloc([(test_symbol, Layout::Builtin(Builtin::Int1))]),
|
||||
remainder: env.arena.alloc(stmt),
|
||||
continuation: env.arena.alloc(cond),
|
||||
};
|
||||
|
||||
current_symbol = accum;
|
||||
}
|
||||
|
||||
for ((new_stores, lhs, rhs, layout), accum) in tests.into_iter().rev().zip(accum_it) {
|
||||
let test_symbol = env.unique_symbol();
|
||||
let test = Expr::RunLowLevel(
|
||||
LowLevel::Eq,
|
||||
|
@ -1271,41 +1303,9 @@ fn decide_to_branching<'a>(
|
|||
cond = Stmt::Let(symbol, expr, layout, env.arena.alloc(cond));
|
||||
}
|
||||
|
||||
condition_symbol = current_symbol;
|
||||
current_symbol = accum;
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
// the guard is the final thing that we check, so needs to be layered on first!
|
||||
if let Some((symbol, id, stmt)) = guard {
|
||||
let test_symbol = symbol;
|
||||
let and_expr = Expr::RunLowLevel(
|
||||
LowLevel::And,
|
||||
env.arena.alloc([test_symbol, condition_symbol]),
|
||||
);
|
||||
|
||||
// write to the branching symbol
|
||||
cond = Stmt::Let(
|
||||
current_symbol,
|
||||
and_expr,
|
||||
Layout::Builtin(Builtin::Int1),
|
||||
env.arena.alloc(cond),
|
||||
);
|
||||
|
||||
// calculate the guard value
|
||||
cond = Stmt::Join {
|
||||
id,
|
||||
arguments: &[],
|
||||
remainder: env.arena.alloc(stmt),
|
||||
continuation: env.arena.alloc(cond),
|
||||
};
|
||||
|
||||
condition_symbol = current_symbol;
|
||||
current_symbol = env.unique_symbol();
|
||||
}
|
||||
*/
|
||||
|
||||
cond = Stmt::Let(
|
||||
true_symbol,
|
||||
Expr::Literal(Literal::Bool(true)),
|
||||
|
|
|
@ -553,7 +553,7 @@ impl<'a> Stmt<'a> {
|
|||
) -> Self {
|
||||
let mut layout_cache = LayoutCache::default();
|
||||
|
||||
dbg!(from_can(env, can_expr, procs, &mut layout_cache))
|
||||
from_can(env, can_expr, procs, &mut layout_cache)
|
||||
}
|
||||
pub fn to_doc<'b, D, A>(&'b self, alloc: &'b D, parens: bool) -> DocBuilder<'b, D, A>
|
||||
where
|
||||
|
@ -926,7 +926,9 @@ fn specialize<'a>(
|
|||
|
||||
debug_assert!(matches!(unified, roc_unify::unify::Unified::Success(_)));
|
||||
|
||||
let specialized_body = from_can(env, body, procs, layout_cache);
|
||||
let ret_symbol = env.unique_symbol();
|
||||
let hole = env.arena.alloc(Stmt::Ret(ret_symbol));
|
||||
let specialized_body = with_hole(env, body, procs, layout_cache, ret_symbol, hole);
|
||||
|
||||
// reset subs, so we don't get type errors when specializing for a different signature
|
||||
env.subs.rollback_to(snapshot);
|
||||
|
@ -1031,7 +1033,13 @@ pub fn with_hole<'a>(
|
|||
todo!()
|
||||
}
|
||||
}
|
||||
Var(symbol) => Stmt::Ret(symbol),
|
||||
Var(symbol) => {
|
||||
// A bit ugly, but it does the job
|
||||
match hole {
|
||||
Stmt::Jump(id, _) => Stmt::Jump(*id, env.arena.alloc([symbol])),
|
||||
_ => Stmt::Ret(symbol),
|
||||
}
|
||||
}
|
||||
// Var(symbol) => panic!("reached Var {}", symbol),
|
||||
Tag {
|
||||
variant_var,
|
||||
|
@ -1206,10 +1214,16 @@ pub fn with_hole<'a>(
|
|||
let mut can_fields = Vec::with_capacity_in(fields.len(), env.arena);
|
||||
|
||||
for (label, layout) in sorted_fields.into_iter() {
|
||||
field_symbols.push(env.unique_symbol());
|
||||
field_layouts.push(layout);
|
||||
|
||||
let field = fields.remove(&label).unwrap();
|
||||
can_fields.push(field);
|
||||
let field_symbol = if let roc_can::expr::Expr::Var(symbol) = field.loc_expr.value {
|
||||
field_symbols.push(symbol);
|
||||
can_fields.push(None);
|
||||
} else {
|
||||
field_symbols.push(env.unique_symbol());
|
||||
can_fields.push(Some(field));
|
||||
};
|
||||
}
|
||||
|
||||
// creating a record from the var will unpack it if it's just a single field.
|
||||
|
@ -1220,7 +1234,9 @@ pub fn with_hole<'a>(
|
|||
let field_symbols = field_symbols.into_bump_slice();
|
||||
let mut stmt = Stmt::Let(assigned, Expr::Struct(field_symbols), layout, hole);
|
||||
|
||||
for (field, symbol) in can_fields.into_iter().rev().zip(field_symbols.iter().rev()) {
|
||||
for (opt_field, symbol) in can_fields.into_iter().rev().zip(field_symbols.iter().rev())
|
||||
{
|
||||
if let Some(field) = opt_field {
|
||||
stmt = with_hole(
|
||||
env,
|
||||
field.loc_expr.value,
|
||||
|
@ -1230,6 +1246,7 @@ pub fn with_hole<'a>(
|
|||
env.arena.alloc(stmt),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
stmt
|
||||
}
|
||||
|
@ -1317,7 +1334,7 @@ pub fn with_hole<'a>(
|
|||
branches,
|
||||
layout_cache,
|
||||
procs,
|
||||
Some((id, assigned)),
|
||||
Some(id),
|
||||
);
|
||||
|
||||
// TODO define condition
|
||||
|
@ -1464,7 +1481,27 @@ pub fn with_hole<'a>(
|
|||
|
||||
Accessor { .. } | Update { .. } => todo!("record access/accessor/update"),
|
||||
|
||||
Closure(_, _, _, _, _) => todo!("call"),
|
||||
Closure(ann, name, _, loc_args, boxed_body) => {
|
||||
let (loc_body, ret_var) = *boxed_body;
|
||||
|
||||
match procs.insert_anonymous(env, name, ann, loc_args, loc_body, ret_var, layout_cache)
|
||||
{
|
||||
Ok(layout) => {
|
||||
// TODO should the let have layout Pointer?
|
||||
Stmt::Let(
|
||||
assigned,
|
||||
Expr::FunctionPointer(name, layout.clone()),
|
||||
layout,
|
||||
hole,
|
||||
)
|
||||
}
|
||||
|
||||
Err(_error) => Stmt::RuntimeError(
|
||||
"TODO convert anonymous function error to a RuntimeError string",
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
Call(boxed, loc_args, _) => {
|
||||
let (fn_var, loc_expr, ret_var) = *boxed;
|
||||
|
||||
|
@ -1532,16 +1569,22 @@ pub fn with_hole<'a>(
|
|||
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
||||
});
|
||||
|
||||
let ret_layout = layout_cache
|
||||
.from_var(env.arena, ret_var, env.subs, env.pointer_size)
|
||||
.unwrap_or_else(|err| {
|
||||
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
|
||||
});
|
||||
|
||||
let function_symbol = env.unique_symbol();
|
||||
let arg_symbols = arg_symbols.into_bump_slice();
|
||||
let mut result = Stmt::Let(
|
||||
assigned,
|
||||
Expr::FunctionCall {
|
||||
call_type: CallType::ByPointer(function_symbol),
|
||||
layout: layout.clone(),
|
||||
layout,
|
||||
args: arg_symbols,
|
||||
},
|
||||
layout,
|
||||
ret_layout,
|
||||
arena.alloc(hole),
|
||||
);
|
||||
|
||||
|
@ -1614,7 +1657,7 @@ pub fn with_hole<'a>(
|
|||
|
||||
result
|
||||
}
|
||||
RuntimeError(_) => todo!("runtime error"),
|
||||
RuntimeError(e) => todo!("runtime error {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1680,82 +1723,6 @@ pub fn from_can<'a>(
|
|||
)
|
||||
}
|
||||
|
||||
If {
|
||||
cond_var,
|
||||
branch_var,
|
||||
branches,
|
||||
final_else,
|
||||
} => {
|
||||
let mut expr = from_can(env, final_else.value, procs, layout_cache);
|
||||
let arena = env.arena;
|
||||
|
||||
let ret_layout = layout_cache
|
||||
.from_var(env.arena, branch_var, env.subs, env.pointer_size)
|
||||
.expect("invalid ret_layout");
|
||||
let cond_layout = layout_cache
|
||||
.from_var(env.arena, cond_var, env.subs, env.pointer_size)
|
||||
.expect("invalid cond_layout");
|
||||
|
||||
for (loc_cond, loc_then) in branches.into_iter().rev() {
|
||||
let branching_symbol = env.unique_symbol();
|
||||
let then = from_can(env, loc_then.value, procs, layout_cache);
|
||||
|
||||
let cond_stmt = Stmt::Cond {
|
||||
cond_symbol: branching_symbol,
|
||||
branching_symbol,
|
||||
cond_layout: cond_layout.clone(),
|
||||
branching_layout: cond_layout.clone(),
|
||||
pass: env.arena.alloc(then),
|
||||
fail: env.arena.alloc(expr),
|
||||
ret_layout: ret_layout.clone(),
|
||||
};
|
||||
|
||||
// add condition
|
||||
let hole = env.arena.alloc(cond_stmt);
|
||||
expr = with_hole(
|
||||
env,
|
||||
loc_cond.value,
|
||||
procs,
|
||||
layout_cache,
|
||||
branching_symbol,
|
||||
hole,
|
||||
);
|
||||
}
|
||||
|
||||
expr
|
||||
}
|
||||
When {
|
||||
cond_var,
|
||||
expr_var,
|
||||
region,
|
||||
loc_cond,
|
||||
branches,
|
||||
} => {
|
||||
let cond_symbol = if let roc_can::expr::Expr::Var(symbol) = loc_cond.value {
|
||||
symbol
|
||||
} else {
|
||||
env.unique_symbol()
|
||||
};
|
||||
|
||||
let mono_when = from_can_when(
|
||||
env,
|
||||
cond_var,
|
||||
expr_var,
|
||||
region,
|
||||
cond_symbol,
|
||||
branches,
|
||||
layout_cache,
|
||||
procs,
|
||||
None,
|
||||
);
|
||||
|
||||
if let roc_can::expr::Expr::Var(_) = loc_cond.value {
|
||||
mono_when
|
||||
} else {
|
||||
let hole = env.arena.alloc(mono_when);
|
||||
with_hole(env, loc_cond.value, procs, layout_cache, cond_symbol, hole)
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
let symbol = env.unique_symbol();
|
||||
let hole = env.arena.alloc(Stmt::Ret(symbol));
|
||||
|
@ -1774,7 +1741,7 @@ fn to_opt_branches<'a>(
|
|||
layout_cache: &mut LayoutCache<'a>,
|
||||
) -> std::vec::Vec<(
|
||||
Pattern<'a>,
|
||||
crate::decision_tree2::Guard<'a>,
|
||||
Option<Located<roc_can::expr::Expr>>,
|
||||
roc_can::expr::Expr,
|
||||
)> {
|
||||
debug_assert!(!branches.is_empty());
|
||||
|
@ -1801,10 +1768,12 @@ fn to_opt_branches<'a>(
|
|||
exhaustive_guard.clone(),
|
||||
));
|
||||
|
||||
// TODO implement guard again
|
||||
let mono_guard = crate::decision_tree2::Guard::NoGuard;
|
||||
|
||||
opt_branches.push((mono_pattern, mono_guard, when_branch.value.value.clone()));
|
||||
// TODO remove clone?
|
||||
opt_branches.push((
|
||||
mono_pattern,
|
||||
when_branch.guard.clone(),
|
||||
when_branch.value.value.clone(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1840,7 +1809,7 @@ fn to_opt_branches<'a>(
|
|||
if is_not_exhaustive {
|
||||
opt_branches.push((
|
||||
Pattern::Underscore,
|
||||
crate::decision_tree2::Guard::NoGuard,
|
||||
None,
|
||||
roc_can::expr::Expr::RuntimeError(
|
||||
roc_problem::can::RuntimeError::NonExhaustivePattern,
|
||||
),
|
||||
|
@ -1861,7 +1830,7 @@ fn from_can_when<'a>(
|
|||
branches: std::vec::Vec<roc_can::expr::WhenBranch>,
|
||||
layout_cache: &mut LayoutCache<'a>,
|
||||
procs: &mut Procs<'a>,
|
||||
join_point: Option<(JoinPointId, Symbol)>,
|
||||
join_point: Option<JoinPointId>,
|
||||
) -> Stmt<'a> {
|
||||
if branches.is_empty() {
|
||||
// A when-expression with no branches is a runtime error.
|
||||
|
@ -1887,13 +1856,15 @@ fn from_can_when<'a>(
|
|||
.unwrap_or_else(|err| panic!("TODO turn this into a RuntimeError {:?}", err));
|
||||
|
||||
let arena = env.arena;
|
||||
let it = opt_branches.into_iter().map(|(pattern, guard, can_expr)| {
|
||||
let it = opt_branches
|
||||
.into_iter()
|
||||
.map(|(pattern, opt_guard, can_expr)| {
|
||||
let mut stores = Vec::with_capacity_in(1, env.arena);
|
||||
let res_stores =
|
||||
store_pattern(env, &pattern, cond_symbol, cond_layout.clone(), &mut stores);
|
||||
let mut stmt = match join_point {
|
||||
None => from_can(env, can_expr, procs, layout_cache),
|
||||
Some((id, _symbol)) => {
|
||||
Some(id) => {
|
||||
let symbol = env.unique_symbol();
|
||||
let arguments = bumpalo::vec![in env.arena; symbol].into_bump_slice();
|
||||
let jump = env.arena.alloc(Stmt::Jump(id, arguments));
|
||||
|
@ -1902,17 +1873,37 @@ fn from_can_when<'a>(
|
|||
}
|
||||
};
|
||||
|
||||
use crate::decision_tree2::Guard;
|
||||
match res_stores {
|
||||
Ok(_) => {
|
||||
for (symbol, layout, expr) in stores.iter().rev() {
|
||||
stmt =
|
||||
Stmt::Let(*symbol, expr.clone(), layout.clone(), env.arena.alloc(stmt));
|
||||
}
|
||||
|
||||
let guard = if let Some(loc_expr) = opt_guard {
|
||||
let id = JoinPointId(env.unique_symbol());
|
||||
let symbol = env.unique_symbol();
|
||||
let jump = env.arena.alloc(Stmt::Jump(id, env.arena.alloc([symbol])));
|
||||
|
||||
let mut stmt =
|
||||
with_hole(env, loc_expr.value, procs, layout_cache, symbol, jump);
|
||||
|
||||
// guard must have access to bound values
|
||||
for (symbol, layout, expr) in stores.into_iter().rev() {
|
||||
stmt = Stmt::Let(symbol, expr, layout, env.arena.alloc(stmt));
|
||||
}
|
||||
|
||||
Guard::Guard { id, symbol, stmt }
|
||||
} else {
|
||||
Guard::NoGuard
|
||||
};
|
||||
|
||||
(pattern, guard, stmt)
|
||||
}
|
||||
Err(msg) => (
|
||||
Pattern::Underscore,
|
||||
guard,
|
||||
Guard::NoGuard,
|
||||
Stmt::RuntimeError(env.arena.alloc(msg)),
|
||||
),
|
||||
}
|
||||
|
|
|
@ -1385,7 +1385,7 @@ mod test_mono {
|
|||
compiles_to_ir(
|
||||
r#"
|
||||
when 2 is
|
||||
2 if True -> 42
|
||||
2 if False -> 42
|
||||
_ -> 0
|
||||
"#,
|
||||
indoc!(
|
||||
|
@ -1586,4 +1586,60 @@ mod test_mono {
|
|||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn if_multi_branch() {
|
||||
compiles_to_ir(
|
||||
r#"
|
||||
if True then
|
||||
1
|
||||
else if False then
|
||||
2
|
||||
else
|
||||
3
|
||||
"#,
|
||||
indoc!(
|
||||
r#"
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let Test.3 = lowlevel ListPush #Attr.2 #Attr.3;
|
||||
ret Test.3;
|
||||
|
||||
let Test.4 = 1i64;
|
||||
let Test.1 = Array [Test.4];
|
||||
let Test.2 = 2i64;
|
||||
let Test.0 = CallByName List.5 Test.1 Test.2;
|
||||
ret Test.0;
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn when_on_result() {
|
||||
compiles_to_ir(
|
||||
r#"
|
||||
x : Result Int Int
|
||||
x = Ok 2
|
||||
|
||||
y = when x is
|
||||
Ok 3 -> 1
|
||||
Ok _ -> 2
|
||||
Err _ -> 3
|
||||
y
|
||||
"#,
|
||||
indoc!(
|
||||
r#"
|
||||
procedure List.5 (#Attr.2, #Attr.3):
|
||||
let Test.3 = lowlevel ListPush #Attr.2 #Attr.3;
|
||||
ret Test.3;
|
||||
|
||||
let Test.4 = 1i64;
|
||||
let Test.1 = Array [Test.4];
|
||||
let Test.2 = 2i64;
|
||||
let Test.0 = CallByName List.5 Test.1 Test.2;
|
||||
ret Test.0;
|
||||
"#
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue