Merge remote-tracking branch 'origin/trunk' into mono-lowlevel

This commit is contained in:
Folkert 2021-10-18 11:11:04 +02:00
commit 031c7cc2e2
20 changed files with 447 additions and 165 deletions

View file

@ -28,3 +28,11 @@ Ju Liu <ju@noredink.com>
Peter Fields <pcfields@gmail.com> Peter Fields <pcfields@gmail.com>
Brian J. Cardiff <bcardiff@gmail.com> Brian J. Cardiff <bcardiff@gmail.com>
Basile Henry <bjm.henry@gmail.com> Basile Henry <bjm.henry@gmail.com>
Tarjei Skjærset <tskj@tarjei.org>
Brian Hicks <brian@brianthicks.com>
Dan Gieschen Knutson <dan.knutson@gmail.com>
Joshua Hoeflich <joshuaharry411@icloud.com>
Brian Carroll <brian.carroll.ireland@gmail.com>
Kofi Gumbs <h.kofigumbs@gmail.com>
Luiz de Oliveira <luizcarlos1405@gmail.com>
Chelsea Troy <chelsea.dommert@gmail.com>

View file

@ -535,16 +535,21 @@ fn single_tag_union_to_ast<'a>(
tag_name: &TagName, tag_name: &TagName,
payload_vars: &[Variable], payload_vars: &[Variable],
) -> Expr<'a> { ) -> Expr<'a> {
debug_assert_eq!(field_layouts.len(), payload_vars.len());
let arena = env.arena; let arena = env.arena;
let tag_expr = tag_name_to_expr(env, tag_name); let tag_expr = tag_name_to_expr(env, tag_name);
let loc_tag_expr = &*arena.alloc(Located::at_zero(tag_expr)); let loc_tag_expr = &*arena.alloc(Located::at_zero(tag_expr));
let it = payload_vars.iter().copied().zip(field_layouts); let output = if field_layouts.len() == payload_vars.len() {
let output = sequence_of_expr(env, ptr as *const u8, it).into_bump_slice(); let it = payload_vars.iter().copied().zip(field_layouts);
sequence_of_expr(env, ptr as *const u8, it).into_bump_slice()
} else if field_layouts.is_empty() && !payload_vars.is_empty() {
// happens for e.g. `Foo Bar` where unit structures are nested and the inner one is dropped
let it = payload_vars.iter().copied().zip([&Layout::Struct(&[])]);
sequence_of_expr(env, ptr as *const u8, it).into_bump_slice()
} else {
unreachable!()
};
Expr::Apply(loc_tag_expr, output, CalledVia::Space) Expr::Apply(loc_tag_expr, output, CalledVia::Space)
} }

View file

@ -103,6 +103,24 @@ mod repl_eval {
expect_success("299 % 10", "Ok 9 : Result (Int *) [ DivByZero ]*"); expect_success("299 % 10", "Ok 9 : Result (Int *) [ DivByZero ]*");
} }
#[test]
fn num_floor_division_success() {
expect_success("Num.divFloor 4 3", "Ok 1 : Result (Int *) [ DivByZero ]*");
}
#[test]
fn num_floor_division_divby_zero() {
expect_success(
"Num.divFloor 4 0",
"Err DivByZero : Result (Int *) [ DivByZero ]*",
);
}
#[test]
fn num_ceil_division_success() {
expect_success("Num.divCeil 4 3", "Ok 2 : Result (Int *) [ DivByZero ]*")
}
#[test] #[test]
fn bool_in_record() { fn bool_in_record() {
expect_success("{ x: 1 == 1 }", "{ x: True } : { x : Bool }"); expect_success("{ x: 1 == 1 }", "{ x: True } : { x : Bool }");
@ -167,6 +185,11 @@ mod repl_eval {
expect_success("Foo 1 3.14", "Foo 1 3.14 : [ Foo (Num *) (Float *) ]*"); expect_success("Foo 1 3.14", "Foo 1 3.14 : [ Foo (Num *) (Float *) ]*");
} }
#[test]
fn newtype_of_unit() {
expect_success("Foo Bar", "Foo Bar : [ Foo [ Bar ]* ]*");
}
#[test] #[test]
fn tag_with_arguments() { fn tag_with_arguments() {
expect_success("True 1", "True 1 : [ True (Num *) ]*"); expect_success("True 1", "True 1 : [ True (Num *) ]*");
@ -307,7 +330,7 @@ mod repl_eval {
expect_success("Num.addChecked 1 1", "Ok 2 : Result (Num *) [ Overflow ]*"); expect_success("Num.addChecked 1 1", "Ok 2 : Result (Num *) [ Overflow ]*");
expect_success( expect_success(
"Num.addChecked Num.maxInt 1", "Num.addChecked Num.maxInt 1",
"Err (Overflow) : Result I64 [ Overflow ]*", "Err Overflow : Result I64 [ Overflow ]*",
); );
} }
@ -316,7 +339,7 @@ mod repl_eval {
expect_success("Num.subChecked 1 1", "Ok 0 : Result (Num *) [ Overflow ]*"); expect_success("Num.subChecked 1 1", "Ok 0 : Result (Num *) [ Overflow ]*");
expect_success( expect_success(
"Num.subChecked Num.minInt 1", "Num.subChecked Num.minInt 1",
"Err (Overflow) : Result I64 [ Overflow ]*", "Err Overflow : Result I64 [ Overflow ]*",
); );
} }
@ -328,7 +351,7 @@ mod repl_eval {
); );
expect_success( expect_success(
"Num.mulChecked Num.maxInt 2", "Num.mulChecked Num.maxInt 2",
"Err (Overflow) : Result I64 [ Overflow ]*", "Err Overflow : Result I64 [ Overflow ]*",
); );
} }
@ -362,7 +385,7 @@ mod repl_eval {
); );
expect_success( expect_success(
"List.first []", "List.first []",
"Err (ListWasEmpty) : Result * [ ListWasEmpty ]*", "Err ListWasEmpty : Result * [ ListWasEmpty ]*",
); );
} }
@ -375,7 +398,7 @@ mod repl_eval {
expect_success( expect_success(
"List.last []", "List.last []",
"Err (ListWasEmpty) : Result * [ ListWasEmpty ]*", "Err ListWasEmpty : Result * [ ListWasEmpty ]*",
); );
} }

View file

@ -76,6 +76,7 @@ comptime {
exportNumFn(num.atan, "atan"); exportNumFn(num.atan, "atan");
exportNumFn(num.isFinite, "is_finite"); exportNumFn(num.isFinite, "is_finite");
exportNumFn(num.powInt, "pow_int"); exportNumFn(num.powInt, "pow_int");
exportNumFn(num.divCeil, "div_ceil");
exportNumFn(num.acos, "acos"); exportNumFn(num.acos, "acos");
exportNumFn(num.asin, "asin"); exportNumFn(num.asin, "asin");
exportNumFn(num.bytesToU16C, "bytes_to_u16"); exportNumFn(num.bytesToU16C, "bytes_to_u16");

View file

@ -15,6 +15,10 @@ pub fn powInt(base: i64, exp: i64) callconv(.C) i64 {
return @call(.{ .modifier = always_inline }, math.pow, .{ i64, base, exp }); return @call(.{ .modifier = always_inline }, math.pow, .{ i64, base, exp });
} }
pub fn divCeil(numerator: i64, denominator: i64) callconv(.C) i64 {
return @call(.{ .modifier = always_inline }, math.divCeil, .{ i64, numerator, denominator }) catch unreachable;
}
pub fn acos(num: f64) callconv(.C) f64 { pub fn acos(num: f64) callconv(.C) f64 {
return @call(.{ .modifier = always_inline }, math.acos, .{num}); return @call(.{ .modifier = always_inline }, math.acos, .{num});
} }

View file

@ -8,6 +8,7 @@ pub const NUM_ACOS: &str = "roc_builtins.num.acos";
pub const NUM_ATAN: &str = "roc_builtins.num.atan"; pub const NUM_ATAN: &str = "roc_builtins.num.atan";
pub const NUM_IS_FINITE: &str = "roc_builtins.num.is_finite"; pub const NUM_IS_FINITE: &str = "roc_builtins.num.is_finite";
pub const NUM_POW_INT: &str = "roc_builtins.num.pow_int"; pub const NUM_POW_INT: &str = "roc_builtins.num.pow_int";
pub const NUM_DIV_CEIL: &str = "roc_builtins.num.div_ceil";
pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16"; pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16";
pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32"; pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32";
pub const NUM_ROUND: &str = "roc_builtins.num.round"; pub const NUM_ROUND: &str = "roc_builtins.num.round";

View file

@ -293,18 +293,25 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
// minInt : Int range // minInt : Int range
add_type!(Symbol::NUM_MIN_INT, int_type(flex(TVAR1))); add_type!(Symbol::NUM_MIN_INT, int_type(flex(TVAR1)));
// divInt : Int a, Int a -> Result (Int a) [ DivByZero ]*
let div_by_zero = SolvedType::TagUnion( let div_by_zero = SolvedType::TagUnion(
vec![(TagName::Global("DivByZero".into()), vec![])], vec![(TagName::Global("DivByZero".into()), vec![])],
Box::new(SolvedType::Wildcard), Box::new(SolvedType::Wildcard),
); );
// divInt : Int a, Int a -> Result (Int a) [ DivByZero ]*
add_top_level_function_type!( add_top_level_function_type!(
Symbol::NUM_DIV_INT, Symbol::NUM_DIV_INT,
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))], vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())), Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())),
); );
//divCeil: Int a, Int a -> Result (Int a) [ DivByZero ]*
add_top_level_function_type!(
Symbol::NUM_DIV_CEIL,
vec![int_type(flex(TVAR1)), int_type(flex(TVAR1))],
Box::new(result_type(int_type(flex(TVAR1)), div_by_zero.clone())),
);
// bitwiseAnd : Int a, Int a -> Int a // bitwiseAnd : Int a, Int a -> Int a
add_top_level_function_type!( add_top_level_function_type!(
Symbol::NUM_BITWISE_AND, Symbol::NUM_BITWISE_AND,

View file

@ -143,6 +143,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
NUM_TAN => num_tan, NUM_TAN => num_tan,
NUM_DIV_FLOAT => num_div_float, NUM_DIV_FLOAT => num_div_float,
NUM_DIV_INT => num_div_int, NUM_DIV_INT => num_div_int,
NUM_DIV_CEIL => num_div_ceil,
NUM_ABS => num_abs, NUM_ABS => num_abs,
NUM_NEG => num_neg, NUM_NEG => num_neg,
NUM_REM => num_rem, NUM_REM => num_rem,
@ -2844,6 +2845,72 @@ fn num_div_int(symbol: Symbol, var_store: &mut VarStore) -> Def {
) )
} }
/// Num.divCeil : Int a , Int a -> Result (Int a) [ DivByZero ]*
fn num_div_ceil(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh();
let num_var = var_store.fresh();
let unbound_zero_var = var_store.fresh();
let unbound_zero_precision_var = var_store.fresh();
let ret_var = var_store.fresh();
let body = If {
branch_var: ret_var,
cond_var: bool_var,
branches: vec![(
// if-condition
no_region(
// Num.neq denominator 0
RunLowLevel {
op: LowLevel::NotEq,
args: vec![
(num_var, Var(Symbol::ARG_2)),
(
num_var,
int(unbound_zero_var, unbound_zero_precision_var, 0),
),
],
ret_var: bool_var,
},
),
// denominator was not zero
no_region(
// Ok (Int.#divUnchecked numerator denominator)
tag(
"Ok",
vec![
// Num.#divUnchecked numerator denominator
RunLowLevel {
op: LowLevel::NumDivCeilUnchecked,
args: vec![
(num_var, Var(Symbol::ARG_1)),
(num_var, Var(Symbol::ARG_2)),
],
ret_var: num_var,
},
],
var_store,
),
),
)],
final_else: Box::new(
// denominator was zero
no_region(tag(
"Err",
vec![tag("DivByZero", Vec::new(), var_store)],
var_store,
)),
),
};
defn(
symbol,
vec![(num_var, Symbol::ARG_1), (num_var, Symbol::ARG_2)],
var_store,
body,
ret_var,
)
}
/// List.first : List elem -> Result elem [ ListWasEmpty ]* /// List.first : List elem -> Result elem [ ListWasEmpty ]*
/// ///
/// List.first : /// List.first :

View file

@ -18,7 +18,7 @@ use roc_region::all::Located;
/// Just (Just a) /// Just (Just a)
/// List (List a) /// List (List a)
/// reverse (reverse l) /// reverse (reverse l)
#[derive(PartialEq, Eq, Clone, Copy)] #[derive(PartialEq, Eq, Clone, Copy, Debug)]
pub enum Parens { pub enum Parens {
NotNeeded, NotNeeded,
InFunctionType, InFunctionType,

View file

@ -133,9 +133,13 @@ impl<'a> Formattable<'a> for Expr<'a> {
} }
} }
ParensAround(sub_expr) => { ParensAround(sub_expr) => {
buf.push('('); if parens == Parens::NotNeeded && !sub_expr_requests_parens(sub_expr) {
sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent); sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
buf.push(')'); } else {
buf.push('(');
sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
buf.push(')');
}
} }
Str(literal) => { Str(literal) => {
use roc_parse::ast::StrLiteral::*; use roc_parse::ast::StrLiteral::*;
@ -199,7 +203,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
buf.push_str(name); buf.push_str(name);
} }
Apply(loc_expr, loc_args, _) => { Apply(loc_expr, loc_args, _) => {
if apply_needs_parens { if apply_needs_parens && !loc_args.is_empty() {
buf.push('('); buf.push('(');
} }
@ -221,7 +225,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
} }
} }
if apply_needs_parens { if apply_needs_parens && !loc_args.is_empty() {
buf.push(')'); buf.push(')');
} }
} }
@ -315,7 +319,7 @@ impl<'a> Formattable<'a> for Expr<'a> {
buf.push_str(key); buf.push_str(key);
} }
Access(expr, key) => { Access(expr, key) => {
expr.format_with_options(buf, parens, Newlines::Yes, indent); expr.format_with_options(buf, Parens::InApply, Newlines::Yes, indent);
buf.push('.'); buf.push('.');
buf.push_str(key); buf.push_str(key);
} }
@ -394,6 +398,8 @@ fn fmt_bin_ops<'a>(
|| lefts.iter().any(|(expr, _)| expr.value.is_multiline()); || lefts.iter().any(|(expr, _)| expr.value.is_multiline());
for (loc_left_side, loc_bin_op) in lefts { for (loc_left_side, loc_bin_op) in lefts {
let bin_op = loc_bin_op.value;
loc_left_side.format_with_options(buf, apply_needs_parens, Newlines::No, indent); loc_left_side.format_with_options(buf, apply_needs_parens, Newlines::No, indent);
if is_multiline { if is_multiline {
@ -402,7 +408,7 @@ fn fmt_bin_ops<'a>(
buf.push(' '); buf.push(' ');
} }
push_op(buf, loc_bin_op.value); push_op(buf, bin_op);
buf.push(' '); buf.push(' ');
} }
@ -1046,3 +1052,32 @@ fn format_field_multiline<'a, T>(
} }
} }
} }
fn sub_expr_requests_parens(expr: &Expr<'_>) -> bool {
match expr {
Expr::BinOps(left_side, _) => {
left_side
.iter()
.any(|(_, loc_bin_op)| match loc_bin_op.value {
BinOp::Caret
| BinOp::Star
| BinOp::Slash
| BinOp::DoubleSlash
| BinOp::Percent
| BinOp::DoublePercent
| BinOp::Plus
| BinOp::Minus
| BinOp::Equals
| BinOp::NotEquals
| BinOp::LessThan
| BinOp::GreaterThan
| BinOp::LessThanOrEq
| BinOp::GreaterThanOrEq
| BinOp::And
| BinOp::Or => true,
BinOp::Pizza | BinOp::Assignment | BinOp::HasType | BinOp::Backpassing => false,
})
}
_ => false,
}
}

View file

@ -362,32 +362,57 @@ mod test_fmt {
); );
} }
// #[test] #[test]
// fn defs_with_defs() { fn excess_parens() {
// expr_formats_to( expr_formats_to(
// indoc!( indoc!(
// r#" r#"
// x = x = (5)
// y = 4
// z = 8
// w y = ((10))
42
"#
),
indoc!(
r#"
x = 5
y = 10
42
"#
),
);
}
// x #[test]
// "# fn defs_with_defs() {
// ), expr_formats_to(
// indoc!( indoc!(
// r#" r#"
// x = x =
// y = 4 y = 4
// z = 8 z = 8
w
// w x
"#
),
indoc!(
r#"
x =
y = 4
z = 8
// x w
// "#
// ), x
// ); "#
// } ),
);
}
#[test] #[test]
fn comment_between_two_defs() { fn comment_between_two_defs() {
@ -548,15 +573,16 @@ mod test_fmt {
)); ));
} }
// #[test] #[test]
// fn record_field_destructuring() { fn record_field_destructuring() {
// expr_formats_same(indoc!( expr_formats_same(indoc!(
// r#" r#"
// when foo is when foo is
// { x: 5 } -> 42 { x: 5 } ->
// "# 42
// )); "#
// } ));
}
#[test] #[test]
fn record_updating() { fn record_updating() {
@ -917,24 +943,43 @@ mod test_fmt {
"# "#
)); ));
// expr_formats_to( expr_formats_to(
// indoc!( indoc!(
// r#" r#"
// identity = \a identity = \a
// -> a -> a
// identity 41 identity 41
// "# "#
// ), ),
// indoc!( indoc!(
// r#" r#"
// identity = \a -> identity = \a -> a
// a
// identity 41 identity 41
// "# "#
// ), ),
// ); );
expr_formats_to(
indoc!(
r#"
identity = \a
->
a + b
identity 4010
"#
),
indoc!(
r#"
identity = \a ->
a + b
identity 4010
"#
),
);
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
@ -944,19 +989,19 @@ mod test_fmt {
"# "#
)); ));
// expr_formats_same(indoc!( // expr_formats_same(indoc!(
// r#" // r#"
// identity = // identity =
// \{ // \{
// x, // x,
// y // y
// } // }
// -> a // -> a
//
// identity 43
// "#
// ));
// //
// identity 43
// "#
// ));
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
identity = \a, identity = \a,
@ -1212,17 +1257,17 @@ mod test_fmt {
} }
#[test] #[test]
fn multi_line_list_def() { fn multi_line_list_def() {
// expr_formats_same(indoc!( expr_formats_same(indoc!(
// r#" r#"
// l = l =
// [ [
// 1, 1,
// 2 2,
// ] ]
// l l
// "# "#
// )); ));
expr_formats_to( expr_formats_to(
indoc!( indoc!(
@ -1248,32 +1293,32 @@ mod test_fmt {
), ),
); );
// expr_formats_to( expr_formats_to(
// indoc!( indoc!(
// r#" r#"
// results = results =
// # Let's count past 6 # Let's count past 6
// [ [
// Ok 6, Ok 6,
// Err CountError Err CountError
// ] ]
// allOks results allOks results
// "# "#
// ), ),
// indoc!( indoc!(
// r#" r#"
// results = results =
// # Let's count past 6 # Let's count past 6
// [ [
// Ok 6, Ok 6,
// Err CountError Err CountError,
// ] ]
// allOks results allOks results
// "# "#
// ), ),
// ); );
} }
// RECORD LITERALS // RECORD LITERALS
@ -1330,18 +1375,18 @@ mod test_fmt {
#[test] #[test]
fn multi_line_record_def() { fn multi_line_record_def() {
// expr_formats_same(indoc!( expr_formats_same(indoc!(
// r#" r#"
// pos = pos =
// { {
// x: 4, x: 4,
// y: 11, y: 11,
// z: 16 z: 16,
// } }
// pos pos
// "# "#
// )); ));
expr_formats_to( expr_formats_to(
indoc!( indoc!(
@ -2204,7 +2249,7 @@ mod test_fmt {
} }
#[test] #[test]
fn precedence_conflict_exponents() { fn binop_parens() {
expr_formats_same(indoc!( expr_formats_same(indoc!(
r#" r#"
if 4 == (6 ^ 6 ^ 7 ^ 8) then if 4 == (6 ^ 6 ^ 7 ^ 8) then
@ -2213,6 +2258,39 @@ mod test_fmt {
"Naturally" "Naturally"
"# "#
)); ));
expr_formats_same(indoc!(
r#"
if 5 == 1 ^ 1 ^ 1 ^ 1 then
"Not buying it"
else
"True"
"#
));
expr_formats_to(
indoc!(
r#"
if (1 == 1)
&& (2 == 1) && (3 == 2) then
"true"
else
"false"
"#
),
indoc!(
r#"
if
(1 == 1)
&& (2 == 1)
&& (3 == 2)
then
"true"
else
"false"
"#
),
);
} }
#[test] #[test]

View file

@ -5295,8 +5295,8 @@ fn run_low_level<'a, 'ctx, 'env>(
} }
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
| NumIsMultipleOf | NumAddWrap | NumAddChecked | NumDivUnchecked | NumPow | NumPowInt | NumIsMultipleOf | NumAddWrap | NumAddChecked | NumDivUnchecked | NumDivCeilUnchecked
| NumSubWrap | NumSubChecked | NumMulWrap | NumMulChecked => { | NumPow | NumPowInt | NumSubWrap | NumSubChecked | NumMulWrap | NumMulChecked => {
debug_assert_eq!(args.len(), 2); debug_assert_eq!(args.len(), 2);
let (lhs_arg, lhs_layout) = load_symbol_and_layout(scope, &args[0]); let (lhs_arg, lhs_layout) = load_symbol_and_layout(scope, &args[0]);
@ -6041,6 +6041,9 @@ fn build_int_binop<'a, 'ctx, 'env>(
} }
NumDivUnchecked => bd.build_int_signed_div(lhs, rhs, "div_int").into(), NumDivUnchecked => bd.build_int_signed_div(lhs, rhs, "div_int").into(),
NumPowInt => call_bitcode_fn(env, &[lhs.into(), rhs.into()], bitcode::NUM_POW_INT), NumPowInt => call_bitcode_fn(env, &[lhs.into(), rhs.into()], bitcode::NUM_POW_INT),
NumDivCeilUnchecked => {
call_bitcode_fn(env, &[lhs.into(), rhs.into()], bitcode::NUM_DIV_CEIL)
}
NumBitwiseAnd => bd.build_and(lhs, rhs, "int_bitwise_and").into(), NumBitwiseAnd => bd.build_and(lhs, rhs, "int_bitwise_and").into(),
NumBitwiseXor => bd.build_xor(lhs, rhs, "int_bitwise_xor").into(), NumBitwiseXor => bd.build_xor(lhs, rhs, "int_bitwise_xor").into(),
NumBitwiseOr => bd.build_or(lhs, rhs, "int_bitwise_or").into(), NumBitwiseOr => bd.build_or(lhs, rhs, "int_bitwise_or").into(),

View file

@ -736,32 +736,69 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
builder.position_at_end(modification_block); builder.position_at_end(modification_block);
let ptr_type = basic_type_from_layout(env, element_layout).ptr_type(AddressSpace::Generic);
if element_layout.contains_refcounted() { if element_layout.contains_refcounted() {
let ptr_type = basic_type_from_layout(env, element_layout).ptr_type(AddressSpace::Generic);
let (len, ptr) = load_list(env.builder, original_wrapper, ptr_type); let (len, ptr) = load_list(env.builder, original_wrapper, ptr_type);
let loop_fn = |_index, element| { let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, ptr);
modify_refcount_layout_help( let call_mode = mode_to_call_mode(fn_val, mode);
env,
parent,
layout_ids,
mode.to_call_mode(fn_val),
when_recursive,
element,
element_layout,
);
};
incrementing_elem_loop(env, parent, ptr, len, "modify_rc_index", loop_fn); match mode {
Mode::Inc => {
// inc is cheap; we never recurse
refcount_ptr.modify(call_mode, layout, env);
builder.build_unconditional_branch(cont_block);
}
Mode::Dec => {
let do_recurse_block = env.context.append_basic_block(parent, "do_recurse");
let no_recurse_block = env.context.append_basic_block(parent, "no_recurse");
builder.build_conditional_branch(
refcount_ptr.is_1(env),
do_recurse_block,
no_recurse_block,
);
{
env.builder.position_at_end(no_recurse_block);
refcount_ptr.modify(call_mode, layout, env);
builder.build_unconditional_branch(cont_block);
}
{
env.builder.position_at_end(do_recurse_block);
let loop_fn = |_index, element| {
modify_refcount_layout_help(
env,
parent,
layout_ids,
mode.to_call_mode(fn_val),
when_recursive,
element,
element_layout,
);
};
incrementing_elem_loop(env, parent, ptr, len, "modify_rc_index", loop_fn);
builder.build_unconditional_branch(cont_block);
}
}
}
} else {
// just increment/decrement the list itself, don't touch the elements
let (_, ptr) = load_list(env.builder, original_wrapper, ptr_type);
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, ptr);
let call_mode = mode_to_call_mode(fn_val, mode);
refcount_ptr.modify(call_mode, layout, env);
builder.build_unconditional_branch(cont_block);
} }
let refcount_ptr = PointerToRefcount::from_list_wrapper(env, original_wrapper);
let call_mode = mode_to_call_mode(fn_val, mode);
refcount_ptr.modify(call_mode, layout, env);
builder.build_unconditional_branch(cont_block);
builder.position_at_end(cont_block); builder.position_at_end(cont_block);
// this function returns void // this function returns void

View file

@ -71,6 +71,7 @@ pub enum LowLevel {
NumLte, NumLte,
NumCompare, NumCompare,
NumDivUnchecked, NumDivUnchecked,
NumDivCeilUnchecked,
NumRemUnchecked, NumRemUnchecked,
NumIsMultipleOf, NumIsMultipleOf,
NumAbs, NumAbs,
@ -165,6 +166,7 @@ macro_rules! first_order {
| NumLte | NumLte
| NumCompare | NumCompare
| NumDivUnchecked | NumDivUnchecked
| NumDivCeilUnchecked
| NumRemUnchecked | NumRemUnchecked
| NumIsMultipleOf | NumIsMultipleOf
| NumAbs | NumAbs

View file

@ -943,6 +943,7 @@ define_builtins! {
103 NUM_BYTES_TO_U16: "bytesToU16" 103 NUM_BYTES_TO_U16: "bytesToU16"
104 NUM_BYTES_TO_U32: "bytesToU32" 104 NUM_BYTES_TO_U32: "bytesToU32"
105 NUM_CAST_TO_NAT: "#castToNat" 105 NUM_CAST_TO_NAT: "#castToNat"
106 NUM_DIV_CEIL: "divCeil"
} }
2 BOOL: "Bool" => { 2 BOOL: "Bool" => {
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias 0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias

View file

@ -953,9 +953,9 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap | NumSubChecked And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap | NumSubChecked
| NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare | NumMul | NumMulWrap | NumMulChecked | NumGt | NumGte | NumLt | NumLte | NumCompare
| NumDivUnchecked | NumRemUnchecked | NumIsMultipleOf | NumPow | NumPowInt | NumDivUnchecked | NumDivCeilUnchecked | NumRemUnchecked | NumIsMultipleOf | NumPow
| NumBitwiseAnd | NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy | NumPowInt | NumBitwiseAnd | NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy
| NumShiftRightZfBy => arena.alloc_slice_copy(&[irrelevant, irrelevant]), | NumShiftRightBy | NumShiftRightZfBy => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked | NumRound NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked | NumRound
| NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin | NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin

View file

@ -1778,7 +1778,6 @@ fn union_sorted_tags_help_new<'a>(
// just one tag in the union (but with arguments) can be a struct // just one tag in the union (but with arguments) can be a struct
let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena); let mut layouts = Vec::with_capacity_in(tags_vec.len(), arena);
let mut contains_zero_sized = false;
// special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int // special-case NUM_AT_NUM: if its argument is a FlexVar, make it Int
match tag_name { match tag_name {
@ -1791,12 +1790,7 @@ fn union_sorted_tags_help_new<'a>(
let var = subs[var_index]; let var = subs[var_index];
match Layout::from_var(&mut env, var) { match Layout::from_var(&mut env, var) {
Ok(layout) => { Ok(layout) => {
// Drop any zero-sized arguments like {} layouts.push(layout);
if !layout.is_dropped_because_empty() {
layouts.push(layout);
} else {
contains_zero_sized = true;
}
} }
Err(LayoutProblem::UnresolvedTypeVar(_)) => { Err(LayoutProblem::UnresolvedTypeVar(_)) => {
// If we encounter an unbound type var (e.g. `Ok *`) // If we encounter an unbound type var (e.g. `Ok *`)
@ -1821,11 +1815,7 @@ fn union_sorted_tags_help_new<'a>(
}); });
if layouts.is_empty() { if layouts.is_empty() {
if contains_zero_sized { UnionVariant::Unit
UnionVariant::UnitWithArguments
} else {
UnionVariant::Unit
}
} else if opt_rec_var.is_some() { } else if opt_rec_var.is_some() {
UnionVariant::Wrapped(WrappedVariant::NonNullableUnwrapped { UnionVariant::Wrapped(WrappedVariant::NonNullableUnwrapped {
tag_name, tag_name,

View file

@ -3305,6 +3305,18 @@ mod solve_expr {
); );
} }
#[test]
fn div_ceil() {
infer_eq_without_problem(
indoc!(
r#"
Num.divCeil
"#
),
"Int a, Int a -> Result (Int a) [ DivByZero ]*",
);
}
#[test] #[test]
fn pow_int() { fn pow_int() {
infer_eq_without_problem( infer_eq_without_problem(

View file

@ -397,7 +397,15 @@ fn subs_fmt_flat_type(this: &FlatType, subs: &Subs, f: &mut fmt::Formatter) -> f
write!(f, "]<{:?}>", new_ext) write!(f, "]<{:?}>", new_ext)
} }
FlatType::FunctionOrTagUnion(_, _, _) => todo!(), FlatType::FunctionOrTagUnion(tagname_index, symbol, ext) => {
let tagname: &TagName = &subs[*tagname_index];
write!(
f,
"FunctionOrTagUnion({:?}, {:?}, {:?})",
tagname, symbol, ext
)
}
FlatType::RecursiveTagUnion(rec, tags, ext) => { FlatType::RecursiveTagUnion(rec, tags, ext) => {
write!(f, "[ ")?; write!(f, "[ ")?;

View file

@ -77,11 +77,11 @@ export fn roc_panic(c_ptr: *c_void, tag_id: u32) callconv(.C) void {
std.process.exit(0); std.process.exit(0);
} }
export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void{ export fn roc_memcpy(dst: [*]u8, src: [*]u8, size: usize) callconv(.C) void {
return memcpy(dst, src, size); return memcpy(dst, src, size);
} }
export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void{ export fn roc_memset(dst: [*]u8, value: i32, size: usize) callconv(.C) void {
return memset(dst, value, size); return memset(dst, value, size);
} }