Merge branch 'trunk' into dev-backend-base

This commit is contained in:
Richard Feldman 2020-11-26 00:02:15 -05:00 committed by GitHub
commit bc75f46908
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 1625 additions and 1168 deletions

View file

@ -489,9 +489,22 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
), ),
); );
// walkRight : List elem, (elem -> accum -> accum), accum -> accum // walk : List elem, (elem -> accum -> accum), accum -> accum
add_type( add_type(
Symbol::LIST_WALK_RIGHT, Symbol::LIST_WALK,
top_level_function(
vec![
list_type(flex(TVAR1)),
closure(vec![flex(TVAR1), flex(TVAR2)], TVAR3, Box::new(flex(TVAR2))),
flex(TVAR2),
],
Box::new(flex(TVAR2)),
),
);
// walkBackwards : List elem, (elem -> accum -> accum), accum -> accum
add_type(
Symbol::LIST_WALK_BACKWARDS,
top_level_function( top_level_function(
vec![ vec![
list_type(flex(TVAR1)), list_type(flex(TVAR1)),

View file

@ -777,11 +777,38 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
) )
}); });
// walkRight : Attr (* | u) (List (Attr u a)) // walk : Attr (* | u) (List (Attr u a))
// , Attr Shared (Attr u a -> b -> b) // , Attr Shared (Attr u a -> b -> b)
// , b // , b
// -> b // -> b
add_type(Symbol::LIST_WALK_RIGHT, { add_type(Symbol::LIST_WALK, {
let_tvars! { u, a, b, star1, closure };
unique_function(
vec![
SolvedType::Apply(
Symbol::ATTR_ATTR,
vec![
container(star1, vec![u]),
SolvedType::Apply(Symbol::LIST_LIST, vec![attr_type(u, a)]),
],
),
shared(SolvedType::Func(
vec![attr_type(u, a), flex(b)],
Box::new(flex(closure)),
Box::new(flex(b)),
)),
flex(b),
],
flex(b),
)
});
// walkBackwards : Attr (* | u) (List (Attr u a))
// , Attr Shared (Attr u a -> b -> b)
// , b
// -> b
add_type(Symbol::LIST_WALK_BACKWARDS, {
let_tvars! { u, a, b, star1, closure }; let_tvars! { u, a, b, star1, closure };
unique_function( unique_function(

View file

@ -71,7 +71,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
Symbol::LIST_JOIN => list_join, Symbol::LIST_JOIN => list_join,
Symbol::LIST_MAP => list_map, Symbol::LIST_MAP => list_map,
Symbol::LIST_KEEP_IF => list_keep_if, Symbol::LIST_KEEP_IF => list_keep_if,
Symbol::LIST_WALK_RIGHT => list_walk_right, Symbol::LIST_WALK => list_walk,
Symbol::LIST_WALK_BACKWARDS => list_walk_backwards,
Symbol::NUM_ADD => num_add, Symbol::NUM_ADD => num_add,
Symbol::NUM_ADD_CHECKED => num_add_checked, Symbol::NUM_ADD_CHECKED => num_add_checked,
Symbol::NUM_ADD_WRAP => num_add_wrap, Symbol::NUM_ADD_WRAP => num_add_wrap,
@ -1313,14 +1314,43 @@ fn list_join(symbol: Symbol, var_store: &mut VarStore) -> Def {
) )
} }
/// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum /// List.walk : List elem, (elem -> accum -> accum), accum -> accum
fn list_walk_right(symbol: Symbol, var_store: &mut VarStore) -> Def { fn list_walk(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh(); let list_var = var_store.fresh();
let func_var = var_store.fresh(); let func_var = var_store.fresh();
let accum_var = var_store.fresh(); let accum_var = var_store.fresh();
let body = RunLowLevel { let body = RunLowLevel {
op: LowLevel::ListWalkRight, op: LowLevel::ListWalk,
args: vec![
(list_var, Var(Symbol::ARG_1)),
(func_var, Var(Symbol::ARG_2)),
(accum_var, Var(Symbol::ARG_3)),
],
ret_var: accum_var,
};
defn(
symbol,
vec![
(list_var, Symbol::ARG_1),
(func_var, Symbol::ARG_2),
(accum_var, Symbol::ARG_3),
],
var_store,
body,
accum_var,
)
}
/// List.walkBackwards : List elem, (elem -> accum -> accum), accum -> accum
fn list_walk_backwards(symbol: Symbol, var_store: &mut VarStore) -> Def {
let list_var = var_store.fresh();
let func_var = var_store.fresh();
let accum_var = var_store.fresh();
let body = RunLowLevel {
op: LowLevel::ListWalkBackwards,
args: vec![ args: vec![
(list_var, Var(Symbol::ARG_1)), (list_var, Var(Symbol::ARG_1)),
(func_var, Var(Symbol::ARG_2)), (func_var, Var(Symbol::ARG_2)),

View file

@ -1,7 +1,7 @@
use crate::llvm::build_list::{ use crate::llvm::build_list::{
allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains, allocate_list, empty_list, empty_polymorphic_list, list_append, list_concat, list_contains,
list_get_unsafe, list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat, list_get_unsafe, list_join, list_keep_if, list_len, list_map, list_prepend, list_repeat,
list_reverse, list_set, list_single, list_sum, list_walk_right, list_reverse, list_set, list_single, list_sum, list_walk, list_walk_backwards,
}; };
use crate::llvm::build_str::{ use crate::llvm::build_str::{
str_concat, str_count_graphemes, str_len, str_split, str_starts_with, CHAR_LAYOUT, str_concat, str_count_graphemes, str_len, str_split, str_starts_with, CHAR_LAYOUT,
@ -735,7 +735,12 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
// Insert field exprs into struct_val // Insert field exprs into struct_val
for (index, field_val) in field_vals.into_iter().enumerate() { for (index, field_val) in field_vals.into_iter().enumerate() {
struct_val = builder struct_val = builder
.build_insert_value(struct_val, field_val, index as u32, "insert_field") .build_insert_value(
struct_val,
field_val,
index as u32,
"insert_record_field",
)
.unwrap(); .unwrap();
} }
@ -785,7 +790,12 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
// Insert field exprs into struct_val // Insert field exprs into struct_val
for (index, field_val) in field_vals.into_iter().enumerate() { for (index, field_val) in field_vals.into_iter().enumerate() {
struct_val = builder struct_val = builder
.build_insert_value(struct_val, field_val, index as u32, "insert_field") .build_insert_value(
struct_val,
field_val,
index as u32,
"insert_single_tag_field",
)
.unwrap(); .unwrap();
} }
@ -848,7 +858,12 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
// Insert field exprs into struct_val // Insert field exprs into struct_val
for (index, field_val) in field_vals.into_iter().enumerate() { for (index, field_val) in field_vals.into_iter().enumerate() {
struct_val = builder struct_val = builder
.build_insert_value(struct_val, field_val, index as u32, "insert_field") .build_insert_value(
struct_val,
field_val,
index as u32,
"insert_multi_tag_field",
)
.unwrap(); .unwrap();
} }
@ -2492,8 +2507,7 @@ fn run_low_level<'a, 'ctx, 'env>(
list_contains(env, parent, elem, elem_layout, list, list_layout) list_contains(env, parent, elem, elem_layout, list, list_layout)
} }
ListWalkRight => { ListWalk => {
// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum
debug_assert_eq!(args.len(), 3); debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]); let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
@ -2502,7 +2516,28 @@ fn run_low_level<'a, 'ctx, 'env>(
let (default, default_layout) = load_symbol_and_layout(env, scope, &args[2]); let (default, default_layout) = load_symbol_and_layout(env, scope, &args[2]);
list_walk_right( list_walk(
env,
parent,
list,
list_layout,
func,
func_layout,
default,
default_layout,
)
}
ListWalkBackwards => {
// List.walkBackwards : List elem, (elem -> accum -> accum), accum -> accum
debug_assert_eq!(args.len(), 3);
let (list, list_layout) = load_symbol_and_layout(env, scope, &args[0]);
let (func, func_layout) = load_symbol_and_layout(env, scope, &args[1]);
let (default, default_layout) = load_symbol_and_layout(env, scope, &args[2]);
list_walk_backwards(
env, env,
parent, parent,
list, list,

View file

@ -809,9 +809,9 @@ pub fn list_sum<'a, 'ctx, 'env>(
builder.build_load(accum_alloca, "load_final_acum") builder.build_load(accum_alloca, "load_final_acum")
} }
/// List.walkRight : List elem, (elem -> accum -> accum), accum -> accum /// List.walk : List elem, (elem -> accum -> accum), accum -> accum
#[allow(clippy::too_many_arguments)] #[allow(clippy::too_many_arguments)]
pub fn list_walk_right<'a, 'ctx, 'env>( pub fn list_walk<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>, parent: FunctionValue<'ctx>,
list: BasicValueEnum<'ctx>, list: BasicValueEnum<'ctx>,
@ -901,6 +901,98 @@ pub fn list_walk_right<'a, 'ctx, 'env>(
builder.build_load(accum_alloca, "load_final_acum") builder.build_load(accum_alloca, "load_final_acum")
} }
/// List.walkBackwards : List elem, (elem -> accum -> accum), accum -> accum
#[allow(clippy::too_many_arguments)]
pub fn list_walk_backwards<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>,
parent: FunctionValue<'ctx>,
list: BasicValueEnum<'ctx>,
list_layout: &Layout<'a>,
func: BasicValueEnum<'ctx>,
func_layout: &Layout<'a>,
default: BasicValueEnum<'ctx>,
default_layout: &Layout<'a>,
) -> BasicValueEnum<'ctx> {
let ctx = env.context;
let builder = env.builder;
let list_wrapper = list.into_struct_value();
let len = list_len(env.builder, list_wrapper);
let accum_type = basic_type_from_layout(env.arena, ctx, default_layout, env.ptr_bytes);
let accum_alloca = builder.build_alloca(accum_type, "alloca_walk_right_accum");
builder.build_store(accum_alloca, default);
let then_block = ctx.append_basic_block(parent, "then");
let cont_block = ctx.append_basic_block(parent, "branchcont");
let condition = builder.build_int_compare(
IntPredicate::UGT,
len,
ctx.i64_type().const_zero(),
"list_non_empty",
);
builder.build_conditional_branch(condition, then_block, cont_block);
builder.position_at_end(then_block);
match (func, func_layout) {
(BasicValueEnum::PointerValue(func_ptr), Layout::FunctionPointer(_, _)) => {
let elem_layout = match list_layout {
Layout::Builtin(Builtin::List(_, layout)) => layout,
_ => unreachable!("can only fold over a list"),
};
let elem_type = basic_type_from_layout(env.arena, ctx, elem_layout, env.ptr_bytes);
let elem_ptr_type = get_ptr_type(&elem_type, AddressSpace::Generic);
let list_ptr = load_list_ptr(builder, list_wrapper, elem_ptr_type);
let walk_right_loop = |_, elem: BasicValueEnum<'ctx>| {
// load current accumulator
let current = builder.build_load(accum_alloca, "retrieve_accum");
let call_site_value =
builder.build_call(func_ptr, &[elem, current], "#walk_right_func");
// set the calling convention explicitly for this call
call_site_value.set_call_convention(crate::llvm::build::FAST_CALL_CONV);
let new_current = call_site_value
.try_as_basic_value()
.left()
.unwrap_or_else(|| panic!("LLVM error: Invalid call by pointer."));
builder.build_store(accum_alloca, new_current);
};
decrementing_elem_loop(
builder,
ctx,
parent,
list_ptr,
len,
"#index",
walk_right_loop,
);
}
_ => {
unreachable!(
"Invalid function basic value enum or layout for List.keepIf : {:?}",
(func, func_layout)
);
}
}
builder.build_unconditional_branch(cont_block);
builder.position_at_end(cont_block);
builder.build_load(accum_alloca, "load_final_acum")
}
/// List.contains : List elem, elem -> Bool /// List.contains : List elem, elem -> Bool
pub fn list_contains<'a, 'ctx, 'env>( pub fn list_contains<'a, 'ctx, 'env>(
env: &Env<'a, 'ctx, 'env>, env: &Env<'a, 'ctx, 'env>,
@ -1537,6 +1629,7 @@ where
let current_index = builder let current_index = builder
.build_load(index_alloca, index_name) .build_load(index_alloca, index_name)
.into_int_value(); .into_int_value();
let next_index = builder.build_int_sub(current_index, one, "nextindex"); let next_index = builder.build_int_sub(current_index, one, "nextindex");
builder.build_store(index_alloca, next_index); builder.build_store(index_alloca, next_index);
@ -1546,7 +1639,7 @@ where
// #index >= 0 // #index >= 0
let condition = builder.build_int_compare( let condition = builder.build_int_compare(
IntPredicate::UGE, IntPredicate::SGE,
next_index, next_index,
ctx.i64_type().const_zero(), ctx.i64_type().const_zero(),
"bounds_check", "bounds_check",

View file

@ -237,11 +237,11 @@ mod gen_list {
} }
#[test] #[test]
fn list_walk_right_empty_all_inline() { fn list_walk_backwards_empty_all_inline() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
List.walkRight [0x1] (\a, b -> a + b) 0 List.walkBackwards [0x1] (\a, b -> a + b) 0
"# "#
), ),
1, 1,
@ -255,7 +255,7 @@ mod gen_list {
empty = empty =
[] []
List.walkRight empty (\a, b -> a + b) 0 List.walkBackwards empty (\a, b -> a + b) 0
"# "#
), ),
0, 0,
@ -264,22 +264,22 @@ mod gen_list {
} }
#[test] #[test]
fn list_walk_right_with_str() { fn list_walk_backwards_with_str() {
assert_evals_to!( assert_evals_to!(
r#"List.walkRight [ "x", "y", "z" ] Str.concat "<""#, r#"List.walkBackwards [ "x", "y", "z" ] Str.concat "<""#,
RocStr::from("zyx<"), RocStr::from("xyz<"),
RocStr RocStr
); );
assert_evals_to!( assert_evals_to!(
r#"List.walkRight [ "Third", "Second", "First" ] Str.concat "Fourth""#, r#"List.walkBackwards [ "Third", "Second", "First" ] Str.concat "Fourth""#,
RocStr::from("FirstSecondThirdFourth"), RocStr::from("ThirdSecondFirstFourth"),
RocStr RocStr
); );
} }
#[test] #[test]
fn list_walk_right_with_record() { fn list_walk_backwards_with_record() {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
@ -295,7 +295,7 @@ mod gen_list {
Zero -> { r & zeroes: r.zeroes + 1 } Zero -> { r & zeroes: r.zeroes + 1 }
One -> { r & ones: r.ones + 1 } One -> { r & ones: r.ones + 1 }
finalCounts = List.walkRight byte acc initialCounts finalCounts = List.walkBackwards byte acc initialCounts
finalCounts.ones * 10 + finalCounts.zeroes finalCounts.ones * 10 + finalCounts.zeroes
"# "#
@ -305,6 +305,26 @@ mod gen_list {
); );
} }
#[test]
fn list_walk_with_str() {
assert_evals_to!(
r#"List.walk [ "x", "y", "z" ] Str.concat "<""#,
RocStr::from("zyx<"),
RocStr
);
assert_evals_to!(
r#"List.walk [ "Third", "Second", "First" ] Str.concat "Fourth""#,
RocStr::from("FirstSecondThirdFourth"),
RocStr
);
}
#[test]
fn list_walk_substraction() {
assert_evals_to!(r#"List.walk [ 1, 2 ] Num.sub 1"#, 2, i64);
}
#[test] #[test]
fn list_keep_if_empty_list_of_int() { fn list_keep_if_empty_list_of_int() {
assert_evals_to!( assert_evals_to!(

View file

@ -844,4 +844,49 @@ mod gen_records {
(bool, bool) (bool, bool)
); );
} }
#[test]
fn alignment_in_record() {
assert_evals_to!(
indoc!("{ c: 32, b: if True then Red else if True then Green else Blue, a: 1 == 1 }"),
(32i64, true, 2u8),
(i64, bool, u8)
);
}
#[test]
fn blue_and_present() {
assert_evals_to!(
indoc!(
r#"
f = \r ->
when r is
{ x: Blue, y ? 3 } -> y
{ x: Red, y ? 5 } -> y
f { x: Blue, y: 7 }
"#
),
7,
i64
);
}
#[test]
fn blue_and_absent() {
assert_evals_to!(
indoc!(
r#"
f = \r ->
when r is
{ x: Blue, y ? 3 } -> y
{ x: Red, y ? 5 } -> y
f { x: Blue }
"#
),
3,
i64
);
}
} }

View file

@ -481,7 +481,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
wrapper = \{} -> wrapper = \{} ->
when 2 is when 2 is
2 if False -> 0 2 if False -> 0
_ -> 42 _ -> 42
@ -499,7 +499,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
wrapper = \{} -> wrapper = \{} ->
when 2 is when 2 is
2 if True -> 42 2 if True -> 42
_ -> 0 _ -> 0
@ -517,7 +517,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
wrapper = \{} -> wrapper = \{} ->
when 2 is when 2 is
_ if False -> 0 _ if False -> 0
_ -> 42 _ -> 42
@ -637,7 +637,7 @@ mod gen_tags {
x : Maybe (Maybe Int) x : Maybe (Maybe Int)
x = Just (Just 41) x = Just (Just 41)
main = main =
x x
"# "#
), ),
@ -701,11 +701,11 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
wrapper = \{} -> wrapper = \{} ->
x : [ Red, White, Blue ] x : [ Red, White, Blue ]
x = Blue x = Blue
y = y =
when x is when x is
Red -> 1 Red -> 1
White -> 2 White -> 2
@ -726,8 +726,8 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
wrapper = \{} -> wrapper = \{} ->
y = y =
when 1 + 2 is when 1 + 2 is
3 -> 3 3 -> 3
1 -> 1 1 -> 1
@ -745,7 +745,7 @@ mod gen_tags {
assert_evals_to!( assert_evals_to!(
indoc!( indoc!(
r#" r#"
y = y =
if 1 + 2 > 0 then if 1 + 2 > 0 then
3 3
else else
@ -758,4 +758,114 @@ mod gen_tags {
i64 i64
); );
} }
#[test]
fn alignment_in_single_tag_construction() {
assert_evals_to!(indoc!("Three (1 == 1) 32"), (32i64, true), (i64, bool));
assert_evals_to!(
indoc!("Three (1 == 1) (if True then Red else if True then Green else Blue) 32"),
(32i64, true, 2u8),
(i64, bool, u8)
);
}
#[test]
fn alignment_in_single_tag_pattern_match() {
assert_evals_to!(
indoc!(
r"#
x = Three (1 == 1) 32
when x is
Three bool int ->
{ bool, int }
#"
),
(32i64, true),
(i64, bool)
);
assert_evals_to!(
indoc!(
r"#
x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32
when x is
Three bool color int ->
{ bool, color, int }
#"
),
(32i64, true, 2u8),
(i64, bool, u8)
);
}
#[test]
fn alignment_in_multi_tag_construction() {
assert_evals_to!(
indoc!(
r"#
x : [ Three Bool Int, Empty ]
x = Three (1 == 1) 32
x
#"
),
(1, 32i64, true),
(i64, i64, bool)
);
assert_evals_to!(
indoc!(
r"#
x : [ Three Bool [ Red, Green, Blue ] Int, Empty ]
x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32
x
#"
),
(1, 32i64, true, 2u8),
(i64, i64, bool, u8)
);
}
#[test]
fn alignment_in_multi_tag_pattern_match() {
assert_evals_to!(
indoc!(
r"#
x : [ Three Bool Int, Empty ]
x = Three (1 == 1) 32
when x is
Three bool int ->
{ bool, int }
Empty ->
{ bool: False, int: 0 }
#"
),
(32i64, true),
(i64, bool)
);
assert_evals_to!(
indoc!(
r"#
x : [ Three Bool [ Red, Green, Blue ] Int, Empty ]
x = Three (1 == 1) (if True then Red else if True then Green else Blue) 32
when x is
Three bool color int ->
{ bool, color, int }
Empty ->
{ bool: False, color: Red, int: 0 }
#"
),
(32i64, true, 2u8),
(i64, bool, u8)
);
}
} }

View file

@ -22,7 +22,8 @@ pub enum LowLevel {
ListJoin, ListJoin,
ListMap, ListMap,
ListKeepIf, ListKeepIf,
ListWalkRight, ListWalk,
ListWalkBackwards,
ListSum, ListSum,
NumAdd, NumAdd,
NumAddWrap, NumAddWrap,

View file

@ -683,18 +683,18 @@ define_builtins! {
5 LIST_APPEND: "append" 5 LIST_APPEND: "append"
6 LIST_MAP: "map" 6 LIST_MAP: "map"
7 LIST_LEN: "len" 7 LIST_LEN: "len"
8 LIST_FOLDL: "foldl" 8 LIST_WALK_BACKWARDS: "walkBackwards"
9 LIST_WALK_RIGHT: "walkRight" 9 LIST_CONCAT: "concat"
10 LIST_CONCAT: "concat" 10 LIST_FIRST: "first"
11 LIST_FIRST: "first" 11 LIST_SINGLE: "single"
12 LIST_SINGLE: "single" 12 LIST_REPEAT: "repeat"
13 LIST_REPEAT: "repeat" 13 LIST_REVERSE: "reverse"
14 LIST_REVERSE: "reverse" 14 LIST_PREPEND: "prepend"
15 LIST_PREPEND: "prepend" 15 LIST_JOIN: "join"
16 LIST_JOIN: "join" 16 LIST_KEEP_IF: "keepIf"
17 LIST_KEEP_IF: "keepIf" 17 LIST_CONTAINS: "contains"
18 LIST_CONTAINS: "contains" 18 LIST_SUM: "sum"
19 LIST_SUM: "sum" 19 LIST_WALK: "walk"
} }
5 RESULT: "Result" => { 5 RESULT: "Result" => {
0 RESULT_RESULT: "Result" imported // the Result.Result type alias 0 RESULT_RESULT: "Result" imported // the Result.Result type alias

View file

@ -535,7 +535,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
ListMap => arena.alloc_slice_copy(&[owned, irrelevant]), ListMap => arena.alloc_slice_copy(&[owned, irrelevant]),
ListKeepIf => arena.alloc_slice_copy(&[owned, irrelevant]), ListKeepIf => arena.alloc_slice_copy(&[owned, irrelevant]),
ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]), ListContains => arena.alloc_slice_copy(&[borrowed, irrelevant]),
ListWalkRight => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]), ListWalk => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
ListWalkBackwards => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
ListSum => arena.alloc_slice_copy(&[borrowed]), ListSum => arena.alloc_slice_copy(&[borrowed]),
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumMul | NumGt Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumMul | NumGt

View file

@ -412,7 +412,7 @@ fn test_at_path<'a>(selected_path: &Path, branch: &Branch<'a>, all_tests: &mut V
arguments.push((Pattern::Underscore, destruct.layout.clone())); arguments.push((Pattern::Underscore, destruct.layout.clone()));
} }
DestructType::Optional(_expr) => { DestructType::Optional(_expr) => {
arguments.push((Pattern::Underscore, destruct.layout.clone())); // do nothing
} }
} }
} }
@ -540,23 +540,27 @@ fn to_relevant_branch_help<'a>(
.. ..
} => { } => {
debug_assert!(test_name == &TagName::Global(RECORD_TAG_NAME.into())); debug_assert!(test_name == &TagName::Global(RECORD_TAG_NAME.into()));
let sub_positions = destructs.into_iter().enumerate().map(|(index, destruct)| { let sub_positions = destructs
let pattern = match destruct.typ { .into_iter()
DestructType::Guard(guard) => guard.clone(), .filter(|destruct| !matches!(destruct.typ, DestructType::Optional(_)))
DestructType::Required => Pattern::Underscore, .enumerate()
DestructType::Optional(_expr) => Pattern::Underscore, .map(|(index, destruct)| {
}; let pattern = match destruct.typ {
DestructType::Guard(guard) => guard.clone(),
DestructType::Required => Pattern::Underscore,
DestructType::Optional(_expr) => unreachable!("because of the filter"),
};
( (
Path::Index { Path::Index {
index: index as u64, index: index as u64,
tag_id: *tag_id, tag_id: *tag_id,
path: Box::new(path.clone()), path: Box::new(path.clone()),
}, },
Guard::NoGuard, Guard::NoGuard,
pattern, pattern,
) )
}); });
start.extend(sub_positions); start.extend(sub_positions);
start.extend(end); start.extend(end);

View file

@ -907,7 +907,13 @@ where
if PRETTY_PRINT_IR_SYMBOLS { if PRETTY_PRINT_IR_SYMBOLS {
alloc.text(format!("{:?}", symbol)) alloc.text(format!("{:?}", symbol))
} else { } else {
alloc.text(format!("{}", symbol)) let text = format!("{}", symbol);
if text.starts_with('.') {
alloc.text("Test").append(text)
} else {
alloc.text(text)
}
} }
} }
@ -917,7 +923,7 @@ where
D::Doc: Clone, D::Doc: Clone,
A: Clone, A: Clone,
{ {
alloc.text(format!("{}", symbol.0)) symbol_to_doc(alloc, symbol.0)
} }
impl<'a> Expr<'a> { impl<'a> Expr<'a> {
@ -1101,7 +1107,9 @@ impl<'a> Stmt<'a> {
.chain(std::iter::once(default_doc)); .chain(std::iter::once(default_doc));
// //
alloc alloc
.text(format!("switch {}:", cond_symbol)) .text("switch ")
.append(symbol_to_doc(alloc, *cond_symbol))
.append(":")
.append(alloc.hardline()) .append(alloc.hardline())
.append( .append(
alloc.intersperse(branches_docs, alloc.hardline().append(alloc.hardline())), alloc.intersperse(branches_docs, alloc.hardline().append(alloc.hardline())),
@ -1115,7 +1123,9 @@ impl<'a> Stmt<'a> {
fail, fail,
.. ..
} => alloc } => alloc
.text(format!("if {} then", branching_symbol)) .text("if ")
.append(symbol_to_doc(alloc, *branching_symbol))
.append(" then")
.append(alloc.hardline()) .append(alloc.hardline())
.append(pass.to_doc(alloc).indent(4)) .append(pass.to_doc(alloc).indent(4))
.append(alloc.hardline()) .append(alloc.hardline())
@ -2384,7 +2394,7 @@ pub fn with_hole<'a>(
Tag { Tag {
variant_var, variant_var,
name: tag_name, name: tag_name,
arguments: args, arguments: mut args,
.. ..
} => { } => {
use crate::layout::UnionVariant::*; use crate::layout::UnionVariant::*;
@ -2421,11 +2431,34 @@ pub fn with_hole<'a>(
} }
Unwrapped(field_layouts) => { Unwrapped(field_layouts) => {
let mut field_symbols_temp =
Vec::with_capacity_in(field_layouts.len(), env.arena);
for (var, arg) in args.drain(..) {
// Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field
let layout = layout_cache
.from_var(env.arena, var, env.subs)
.unwrap_or_else(|err| {
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
});
let alignment = layout.alignment_bytes(8);
let symbol = possible_reuse_symbol(env, procs, &arg.value);
field_symbols_temp.push((
alignment,
symbol,
((var, arg), &*env.arena.alloc(symbol)),
));
}
field_symbols_temp.sort_by(|a, b| b.0.cmp(&a.0));
let mut field_symbols = Vec::with_capacity_in(field_layouts.len(), env.arena); let mut field_symbols = Vec::with_capacity_in(field_layouts.len(), env.arena);
for (_, arg) in args.iter() { for (_, symbol, _) in field_symbols_temp.iter() {
field_symbols.push(possible_reuse_symbol(env, procs, &arg.value)); field_symbols.push(*symbol);
} }
let field_symbols = field_symbols.into_bump_slice(); let field_symbols = field_symbols.into_bump_slice();
// Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field // Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field
@ -2438,7 +2471,7 @@ pub fn with_hole<'a>(
// even though this was originally a Tag, we treat it as a Struct from now on // even though this was originally a Tag, we treat it as a Struct from now on
let stmt = Stmt::Let(assigned, Expr::Struct(field_symbols), layout, hole); let stmt = Stmt::Let(assigned, Expr::Struct(field_symbols), layout, hole);
let iter = args.into_iter().rev().zip(field_symbols.iter().rev()); let iter = field_symbols_temp.into_iter().map(|(_, _, data)| data);
assign_to_symbols(env, procs, layout_cache, iter, stmt) assign_to_symbols(env, procs, layout_cache, iter, stmt)
} }
Wrapped(sorted_tag_layouts) => { Wrapped(sorted_tag_layouts) => {
@ -2449,12 +2482,33 @@ pub fn with_hole<'a>(
.find(|(_, (key, _))| key == &tag_name) .find(|(_, (key, _))| key == &tag_name)
.expect("tag must be in its own type"); .expect("tag must be in its own type");
let mut field_symbols_temp = Vec::with_capacity_in(args.len(), env.arena);
for (var, arg) in args.drain(..) {
// Layout will unpack this unwrapped tack if it only has one (non-zero-sized) field
let layout = layout_cache
.from_var(env.arena, var, env.subs)
.unwrap_or_else(|err| {
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
});
let alignment = layout.alignment_bytes(8);
let symbol = possible_reuse_symbol(env, procs, &arg.value);
field_symbols_temp.push((
alignment,
symbol,
((var, arg), &*env.arena.alloc(symbol)),
));
}
field_symbols_temp.sort_by(|a, b| b.0.cmp(&a.0));
let mut field_symbols: Vec<Symbol> = Vec::with_capacity_in(args.len(), arena); let mut field_symbols: Vec<Symbol> = Vec::with_capacity_in(args.len(), arena);
let tag_id_symbol = env.unique_symbol(); let tag_id_symbol = env.unique_symbol();
field_symbols.push(tag_id_symbol); field_symbols.push(tag_id_symbol);
for (_, arg) in args.iter() { for (_, symbol, _) in field_symbols_temp.iter() {
field_symbols.push(possible_reuse_symbol(env, procs, &arg.value)); field_symbols.push(*symbol);
} }
let mut layouts: Vec<&'a [Layout<'a>]> = let mut layouts: Vec<&'a [Layout<'a>]> =
@ -2475,7 +2529,11 @@ pub fn with_hole<'a>(
}; };
let mut stmt = Stmt::Let(assigned, tag, layout, hole); let mut stmt = Stmt::Let(assigned, tag, layout, hole);
let iter = args.into_iter().rev().zip(field_symbols.iter().rev()); let iter = field_symbols_temp
.drain(..)
.map(|x| x.2 .0)
.rev()
.zip(field_symbols.iter().rev());
stmt = assign_to_symbols(env, procs, layout_cache, iter, stmt); stmt = assign_to_symbols(env, procs, layout_cache, iter, stmt);
@ -5290,6 +5348,20 @@ pub fn from_can_pattern<'a>(
}], }],
}; };
let mut arguments = arguments.clone();
arguments.sort_by(|arg1, arg2| {
let ptr_bytes = 8;
let layout1 = layout_cache.from_var(env.arena, arg1.0, env.subs).unwrap();
let layout2 = layout_cache.from_var(env.arena, arg2.0, env.subs).unwrap();
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
size2.cmp(&size1)
});
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena); let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
for ((_, loc_pat), layout) in arguments.iter().zip(field_layouts.iter()) { for ((_, loc_pat), layout) in arguments.iter().zip(field_layouts.iter()) {
mono_args.push(( mono_args.push((
@ -5333,6 +5405,20 @@ pub fn from_can_pattern<'a>(
let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena); let mut mono_args = Vec::with_capacity_in(arguments.len(), env.arena);
// disregard the tag discriminant layout // disregard the tag discriminant layout
let mut arguments = arguments.clone();
arguments.sort_by(|arg1, arg2| {
let ptr_bytes = 8;
let layout1 = layout_cache.from_var(env.arena, arg1.0, env.subs).unwrap();
let layout2 = layout_cache.from_var(env.arena, arg2.0, env.subs).unwrap();
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
size2.cmp(&size1)
});
// TODO make this assert pass, it currently does not because // TODO make this assert pass, it currently does not because
// 0-sized values are dropped out // 0-sized values are dropped out
// debug_assert_eq!(arguments.len(), argument_layouts[1..].len()); // debug_assert_eq!(arguments.len(), argument_layouts[1..].len());
@ -5374,8 +5460,8 @@ pub fn from_can_pattern<'a>(
// sorted fields based on the destruct // sorted fields based on the destruct
let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena); let mut mono_destructs = Vec::with_capacity_in(destructs.len(), env.arena);
let mut destructs = destructs.clone(); let destructs_by_label = env.arena.alloc(MutMap::default());
destructs.sort_by(|a, b| a.value.label.cmp(&b.value.label)); destructs_by_label.extend(destructs.iter().map(|x| (&x.value.label, x)));
let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena); let mut field_layouts = Vec::with_capacity_in(sorted_fields.len(), env.arena);
@ -5387,119 +5473,96 @@ pub fn from_can_pattern<'a>(
// in the source the field is not matche in the source language. // in the source the field is not matche in the source language.
// //
// Optional fields somewhat complicate the matter here // Optional fields somewhat complicate the matter here
let mut it1 = sorted_fields.into_iter();
let mut opt_sorted = it1.next();
let mut it2 = destructs.iter(); for (label, variable, res_layout) in sorted_fields.into_iter() {
let mut opt_destruct = it2.next(); match res_layout {
Ok(field_layout) => {
// the field is non-optional according to the type
loop { match destructs_by_label.remove(&label) {
match (opt_sorted, opt_destruct) { Some(destruct) => {
(Some((label, variable, Ok(field_layout))), Some(destruct)) => { // this field is destructured by the pattern
if destruct.value.label == label { mono_destructs.push(from_can_record_destruct(
mono_destructs.push(from_can_record_destruct( env,
env, layout_cache,
layout_cache, &destruct.value,
&destruct.value, field_layout.clone(),
field_layout.clone(), ));
)); }
None => {
opt_sorted = it1.next(); // this field is not destructured by the pattern
opt_destruct = it2.next(); // put in an underscore
} else { mono_destructs.push(RecordDestruct {
// insert underscore pattern label: label.clone(),
mono_destructs.push(RecordDestruct { symbol: env.unique_symbol(),
label: label.clone(), variable,
symbol: env.unique_symbol(), layout: field_layout.clone(),
variable, typ: DestructType::Guard(Pattern::Underscore),
layout: field_layout.clone(), });
typ: DestructType::Guard(Pattern::Underscore), }
});
opt_sorted = it1.next();
} }
// the layout of this field is part of the layout of the record
field_layouts.push(field_layout); field_layouts.push(field_layout);
} }
(Some((label, variable, Err(field_layout))), Some(destruct)) => { Err(field_layout) => {
if destruct.value.label == label { // the field is optional according to the type
opt_destruct = it2.next(); match destructs_by_label.remove(&label) {
Some(destruct) => {
mono_destructs.push(RecordDestruct { // this field is destructured by the pattern
label: destruct.value.label.clone(),
symbol: destruct.value.symbol,
layout: field_layout,
variable,
typ: match &destruct.value.typ {
roc_can::pattern::DestructType::Optional(_, loc_expr) => {
// if we reach this stage, the optional field is not present
// so use the default
DestructType::Optional(loc_expr.value.clone())
}
_ => unreachable!(
"only optional destructs can be optional fields"
),
},
});
}
opt_sorted = it1.next();
}
(Some((label, variable, Err(field_layout))), None) => {
// the remainder of the fields (from the type) is not matched on in
// this pattern; to fill it out, we put underscores
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
variable,
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
});
opt_sorted = it1.next();
}
(Some((label, variable, Ok(field_layout))), None) => {
// the remainder of the fields (from the type) is not matched on in
// this pattern; to fill it out, we put underscores
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
variable,
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
});
field_layouts.push(field_layout);
opt_sorted = it1.next();
}
(None, Some(destruct)) => {
// destruct is not in the type, but is in the pattern
// it must be an optional field, and we will use the default
match &destruct.value.typ {
roc_can::pattern::DestructType::Optional(field_var, loc_expr) => {
let field_layout = layout_cache
.from_var(env.arena, *field_var, env.subs)
.unwrap_or_else(|err| {
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
});
mono_destructs.push(RecordDestruct { mono_destructs.push(RecordDestruct {
label: destruct.value.label.clone(), label: destruct.value.label.clone(),
symbol: destruct.value.symbol, symbol: destruct.value.symbol,
variable: destruct.value.var,
layout: field_layout, layout: field_layout,
typ: DestructType::Optional(loc_expr.value.clone()), variable,
}) typ: match &destruct.value.typ {
roc_can::pattern::DestructType::Optional(_, loc_expr) => {
// if we reach this stage, the optional field is not present
// so use the default
DestructType::Optional(loc_expr.value.clone())
}
_ => unreachable!(
"only optional destructs can be optional fields"
),
},
});
}
None => {
// this field is not destructured by the pattern
// put in an underscore
mono_destructs.push(RecordDestruct {
label: label.clone(),
symbol: env.unique_symbol(),
variable,
layout: field_layout.clone(),
typ: DestructType::Guard(Pattern::Underscore),
});
} }
_ => unreachable!("only optional destructs can be optional fields"),
} }
}
}
}
opt_sorted = None; for (_, destruct) in destructs_by_label.drain() {
opt_destruct = it2.next(); // this destruct is not in the type, but is in the pattern
} // it must be an optional field, and we will use the default
(None, None) => { match &destruct.value.typ {
break; roc_can::pattern::DestructType::Optional(field_var, loc_expr) => {
let field_layout = layout_cache
.from_var(env.arena, *field_var, env.subs)
.unwrap_or_else(|err| {
panic!("TODO turn fn_var into a RuntimeError {:?}", err)
});
mono_destructs.push(RecordDestruct {
label: destruct.value.label.clone(),
symbol: destruct.value.symbol,
variable: destruct.value.var,
layout: field_layout,
typ: DestructType::Optional(loc_expr.value.clone()),
})
} }
_ => unreachable!("only optional destructs can be optional fields"),
} }
} }

View file

@ -4,6 +4,7 @@ use roc_collections::all::{default_hasher, MutMap, MutSet};
use roc_module::ident::{Lowercase, TagName}; use roc_module::ident::{Lowercase, TagName};
use roc_module::symbol::{Interns, Symbol}; use roc_module::symbol::{Interns, Symbol};
use roc_types::subs::{Content, FlatType, Subs, Variable}; use roc_types::subs::{Content, FlatType, Subs, Variable};
use roc_types::types::RecordField;
use std::collections::HashMap; use std::collections::HashMap;
pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::<u8>() * 8) as usize; pub const MAX_ENUM_SIZE: usize = (std::mem::size_of::<u8>() * 8) as usize;
@ -789,59 +790,30 @@ fn layout_from_flat_type<'a>(
} }
} }
Record(fields, ext_var) => { Record(fields, ext_var) => {
// Sort the fields by label
let mut sorted_fields = Vec::with_capacity_in(fields.len(), arena);
sorted_fields.extend(fields.into_iter());
// extract any values from the ext_var // extract any values from the ext_var
let mut fields_map = MutMap::default(); let mut fields_map = MutMap::default();
fields_map.extend(fields);
match roc_types::pretty_print::chase_ext_record(subs, ext_var, &mut fields_map) { match roc_types::pretty_print::chase_ext_record(subs, ext_var, &mut fields_map) {
Ok(()) | Err((_, Content::FlexVar(_))) => {} Ok(()) | Err((_, Content::FlexVar(_))) => {}
Err(_) => unreachable!("this would have been a type error"), Err(_) => unreachable!("this would have been a type error"),
} }
sorted_fields.extend(fields_map.into_iter()); let sorted_fields = sort_record_fields_help(env, fields_map);
sorted_fields.sort_by(|(label1, _), (label2, _)| label1.cmp(label2));
// Determine the layouts of the fields, maintaining sort order // Determine the layouts of the fields, maintaining sort order
let mut layouts = Vec::with_capacity_in(sorted_fields.len(), arena); let mut layouts = Vec::with_capacity_in(sorted_fields.len(), arena);
for (label, field) in sorted_fields { for (_, _, res_layout) in sorted_fields {
use LayoutProblem::*; match res_layout {
let field_var = {
use roc_types::types::RecordField::*;
match field {
Optional(_) => {
// when an optional field reaches this stage, the field was truly
// optional, and not unified to be demanded or required
// therefore, there is no such field on the record, and we ignore this
// field from now on.
continue;
}
Required(var) => var,
Demanded(var) => var,
}
};
match Layout::from_var(env, field_var) {
Ok(layout) => { Ok(layout) => {
// Drop any zero-sized fields like {}. // Drop any zero-sized fields like {}.
if !layout.is_dropped_because_empty() { if !layout.is_dropped_because_empty() {
layouts.push(layout); layouts.push(layout);
} }
} }
Err(UnresolvedTypeVar(v)) => { Err(_) => {
// Invalid field! // optional field, ignore
panic!( continue;
r"I hit an unresolved type var {:?} when determining the layout of {:?} of record field: {:?} : {:?}",
field_var, v, label, field
);
}
Err(Erroneous) => {
// Invalid field!
panic!("TODO gracefully handle record with invalid field.var");
} }
} }
} }
@ -894,6 +866,15 @@ fn layout_from_flat_type<'a>(
tag_layout.push(Layout::from_var(env, var)?); tag_layout.push(Layout::from_var(env, var)?);
} }
tag_layout.sort_by(|layout1, layout2| {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
size2.cmp(&size1)
});
tag_layouts.push(tag_layout.into_bump_slice()); tag_layouts.push(tag_layout.into_bump_slice());
} }
@ -924,39 +905,55 @@ pub fn sort_record_fields<'a>(
}; };
match roc_types::pretty_print::chase_ext_record(subs, var, &mut fields_map) { match roc_types::pretty_print::chase_ext_record(subs, var, &mut fields_map) {
Ok(()) | Err((_, Content::FlexVar(_))) => { Ok(()) | Err((_, Content::FlexVar(_))) => sort_record_fields_help(&mut env, fields_map),
// Sort the fields by label
let mut sorted_fields = Vec::with_capacity_in(fields_map.len(), arena);
use roc_types::types::RecordField;
for (label, field) in fields_map {
let var = match field {
RecordField::Demanded(v) => v,
RecordField::Required(v) => v,
RecordField::Optional(v) => {
let layout =
Layout::from_var(&mut env, v).expect("invalid layout from var");
sorted_fields.push((label, v, Err(layout)));
continue;
}
};
let layout = Layout::from_var(&mut env, var).expect("invalid layout from var");
// Drop any zero-sized fields like {}
if !layout.is_dropped_because_empty() {
sorted_fields.push((label, var, Ok(layout)));
}
}
sorted_fields.sort_by(|(label1, _, _), (label2, _, _)| label1.cmp(label2));
sorted_fields
}
Err(other) => panic!("invalid content in record variable: {:?}", other), Err(other) => panic!("invalid content in record variable: {:?}", other),
} }
} }
fn sort_record_fields_help<'a>(
env: &mut Env<'a, '_>,
fields_map: MutMap<Lowercase, RecordField<Variable>>,
) -> Vec<'a, (Lowercase, Variable, Result<Layout<'a>, Layout<'a>>)> {
// Sort the fields by label
let mut sorted_fields = Vec::with_capacity_in(fields_map.len(), env.arena);
for (label, field) in fields_map {
let var = match field {
RecordField::Demanded(v) => v,
RecordField::Required(v) => v,
RecordField::Optional(v) => {
let layout = Layout::from_var(env, v).expect("invalid layout from var");
sorted_fields.push((label, v, Err(layout)));
continue;
}
};
let layout = Layout::from_var(env, var).expect("invalid layout from var");
// Drop any zero-sized fields like {}
if !layout.is_dropped_because_empty() {
sorted_fields.push((label, var, Ok(layout)));
}
}
sorted_fields.sort_by(
|(label1, _, res_layout1), (label2, _, res_layout2)| match res_layout1 {
Ok(layout1) | Err(layout1) => match res_layout2 {
Ok(layout2) | Err(layout2) => {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
size2.cmp(&size1).then(label1.cmp(label2))
}
},
},
);
sorted_fields
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)] #[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub enum UnionVariant<'a> { pub enum UnionVariant<'a> {
Never, Never,
@ -1059,6 +1056,15 @@ pub fn union_sorted_tags_help<'a>(
} }
} }
layouts.sort_by(|layout1, layout2| {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
size2.cmp(&size1)
});
if layouts.is_empty() { if layouts.is_empty() {
if contains_zero_sized { if contains_zero_sized {
UnionVariant::UnitWithArguments UnionVariant::UnitWithArguments
@ -1102,6 +1108,15 @@ pub fn union_sorted_tags_help<'a>(
} }
} }
arg_layouts.sort_by(|layout1, layout2| {
let ptr_bytes = 8;
let size1 = layout1.alignment_bytes(ptr_bytes);
let size2 = layout2.alignment_bytes(ptr_bytes);
size2.cmp(&size1)
});
answer.push((tag_name, arg_layouts.into_bump_slice())); answer.push((tag_name, arg_layouts.into_bump_slice()));
} }

File diff suppressed because it is too large Load diff

View file

@ -2928,11 +2928,11 @@ mod solve_expr {
} }
#[test] #[test]
fn list_walk_right() { fn list_walk_backwards() {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
List.walkRight List.walkBackwards
"# "#
), ),
"List a, (a, b -> b), b -> b", "List a, (a, b -> b), b -> b",
@ -2940,7 +2940,7 @@ mod solve_expr {
} }
#[test] #[test]
fn list_walk_right_example() { fn list_walk_backwards_example() {
infer_eq_without_problem( infer_eq_without_problem(
indoc!( indoc!(
r#" r#"
@ -2948,7 +2948,7 @@ mod solve_expr {
empty = empty =
[] []
List.walkRight empty (\a, b -> a + b) 0 List.walkBackwards empty (\a, b -> a + b) 0
"# "#
), ),
"Int", "Int",

View file

@ -2236,11 +2236,11 @@ mod solve_uniq_expr {
} }
#[test] #[test]
fn list_walk_right_sum() { fn list_walk_backwards_sum() {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
sum = \list -> List.walkRight list Num.add 0 sum = \list -> List.walkBackwards list Num.add 0
sum sum
"# "#
@ -2321,11 +2321,11 @@ mod solve_uniq_expr {
} }
#[test] #[test]
fn list_walk_right_reverse() { fn list_walk_backwards_reverse() {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
reverse = \list -> List.walkRight list (\e, l -> List.append l e) [] reverse = \list -> List.walkBackwards list (\e, l -> List.append l e) []
reverse reverse
"# "#
@ -3133,11 +3133,11 @@ mod solve_uniq_expr {
} }
#[test] #[test]
fn list_walk_right() { fn list_walk_backwards() {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
List.walkRight List.walkBackwards
"# "#
), ),
"Attr * (Attr (* | b) (List (Attr b a)), Attr Shared (Attr b a, c -> c), c -> c)", "Attr * (Attr (* | b) (List (Attr b a)), Attr Shared (Attr b a, c -> c), c -> c)",
@ -3145,7 +3145,7 @@ mod solve_uniq_expr {
} }
#[test] #[test]
fn list_walk_right_example() { fn list_walk_backwards_example() {
infer_eq( infer_eq(
indoc!( indoc!(
r#" r#"
@ -3153,7 +3153,7 @@ mod solve_uniq_expr {
empty = empty =
[] []
List.walkRight empty (\a, b -> a + b) 0 List.walkBackwards empty (\a, b -> a + b) 0
"# "#
), ),
"Attr a Int", "Attr a Int",