mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-29 14:54:47 +00:00
Merge remote-tracking branch 'origin/trunk' into mono-lowlevel
This commit is contained in:
commit
031c7cc2e2
20 changed files with 447 additions and 165 deletions
8
AUTHORS
8
AUTHORS
|
@ -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>
|
||||||
|
|
|
@ -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 output = if field_layouts.len() == payload_vars.len() {
|
||||||
let it = payload_vars.iter().copied().zip(field_layouts);
|
let it = payload_vars.iter().copied().zip(field_layouts);
|
||||||
let output = sequence_of_expr(env, ptr as *const u8, it).into_bump_slice();
|
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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 ]*",
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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");
|
||||||
|
|
|
@ -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});
|
||||||
}
|
}
|
||||||
|
|
|
@ -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";
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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 :
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -133,10 +133,14 @@ impl<'a> Formattable<'a> for Expr<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ParensAround(sub_expr) => {
|
ParensAround(sub_expr) => {
|
||||||
|
if parens == Parens::NotNeeded && !sub_expr_requests_parens(sub_expr) {
|
||||||
|
sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||||
|
} else {
|
||||||
buf.push('(');
|
buf.push('(');
|
||||||
sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
sub_expr.format_with_options(buf, Parens::NotNeeded, Newlines::Yes, indent);
|
||||||
buf.push(')');
|
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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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
|
|
||||||
|
|
||||||
// x
|
|
||||||
// "#
|
|
||||||
// ),
|
|
||||||
// indoc!(
|
|
||||||
// r#"
|
|
||||||
// x =
|
|
||||||
// y = 4
|
|
||||||
// z = 8
|
|
||||||
|
|
||||||
// w
|
y = ((10))
|
||||||
|
|
||||||
// x
|
42
|
||||||
// "#
|
"#
|
||||||
// ),
|
),
|
||||||
// );
|
indoc!(
|
||||||
// }
|
r#"
|
||||||
|
x = 5
|
||||||
|
|
||||||
|
y = 10
|
||||||
|
|
||||||
|
42
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn defs_with_defs() {
|
||||||
|
expr_formats_to(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x =
|
||||||
|
y = 4
|
||||||
|
z = 8
|
||||||
|
w
|
||||||
|
|
||||||
|
x
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
x =
|
||||||
|
y = 4
|
||||||
|
z = 8
|
||||||
|
|
||||||
|
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#"
|
||||||
|
@ -956,7 +1001,7 @@ mod test_fmt {
|
||||||
// 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]
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
|
@ -736,11 +736,40 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
builder.position_at_end(modification_block);
|
builder.position_at_end(modification_block);
|
||||||
|
|
||||||
if element_layout.contains_refcounted() {
|
|
||||||
let ptr_type = basic_type_from_layout(env, element_layout).ptr_type(AddressSpace::Generic);
|
let ptr_type = basic_type_from_layout(env, element_layout).ptr_type(AddressSpace::Generic);
|
||||||
|
if element_layout.contains_refcounted() {
|
||||||
let (len, ptr) = load_list(env.builder, original_wrapper, ptr_type);
|
let (len, 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);
|
||||||
|
|
||||||
|
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| {
|
let loop_fn = |_index, element| {
|
||||||
modify_refcount_layout_help(
|
modify_refcount_layout_help(
|
||||||
env,
|
env,
|
||||||
|
@ -754,13 +783,21 @@ fn modify_refcount_list_help<'a, 'ctx, 'env>(
|
||||||
};
|
};
|
||||||
|
|
||||||
incrementing_elem_loop(env, parent, ptr, len, "modify_rc_index", loop_fn);
|
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_list_wrapper(env, original_wrapper);
|
let refcount_ptr = PointerToRefcount::from_ptr_to_data(env, ptr);
|
||||||
let call_mode = mode_to_call_mode(fn_val, mode);
|
let call_mode = mode_to_call_mode(fn_val, mode);
|
||||||
|
|
||||||
refcount_ptr.modify(call_mode, layout, env);
|
refcount_ptr.modify(call_mode, layout, env);
|
||||||
|
|
||||||
builder.build_unconditional_branch(cont_block);
|
builder.build_unconditional_branch(cont_block);
|
||||||
|
}
|
||||||
|
|
||||||
builder.position_at_end(cont_block);
|
builder.position_at_end(cont_block);
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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 {}
|
|
||||||
if !layout.is_dropped_because_empty() {
|
|
||||||
layouts.push(layout);
|
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::UnitWithArguments
|
|
||||||
} else {
|
|
||||||
UnionVariant::Unit
|
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,
|
||||||
|
|
|
@ -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(
|
||||||
|
|
|
@ -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, "[ ")?;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue