mirror of
https://github.com/roc-lang/roc.git
synced 2024-11-13 09:49:11 +03:00
add different variants of addition
This commit is contained in:
parent
455b73e8bd
commit
fb4a796e07
@ -210,6 +210,12 @@ fn build_file(
|
||||
"host.rs",
|
||||
"-o",
|
||||
binary_path.as_path().to_str().unwrap(),
|
||||
// ensure we don't make a position-independent executable
|
||||
"-C",
|
||||
"link-arg=-no-pie",
|
||||
// explicitly link in the c++ stdlib, for exceptions
|
||||
"-C",
|
||||
"link-arg=-lc++",
|
||||
])
|
||||
.current_dir(cwd)
|
||||
.spawn()
|
||||
|
@ -36,3 +36,12 @@ pub fn pow_int_(mut base: i64, mut exp: i64) -> i64 {
|
||||
|
||||
acc
|
||||
}
|
||||
|
||||
/// Adapted from Rust's core::num module, by the Rust core team,
|
||||
/// licensed under the Apache License, version 2.0 - https://www.apache.org/licenses/LICENSE-2.0
|
||||
///
|
||||
/// Thank you, Rust core team!
|
||||
#[no_mangle]
|
||||
pub fn is_finite_(num: f64) -> bool {
|
||||
f64::is_finite(num)
|
||||
}
|
||||
|
@ -188,8 +188,8 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
);
|
||||
|
||||
// addChecked or (+) : Num a, Num a -> Result (Num a) [ IntOverflow ]*
|
||||
let int_overflow = SolvedType::TagUnion(
|
||||
vec![(TagName::Global("IntOverflow".into()), vec![])],
|
||||
let overflow = SolvedType::TagUnion(
|
||||
vec![(TagName::Global("Overflow".into()), vec![])],
|
||||
Box::new(SolvedType::Wildcard),
|
||||
);
|
||||
|
||||
@ -197,7 +197,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
Symbol::NUM_ADD_CHECKED,
|
||||
SolvedType::Func(
|
||||
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||
Box::new(result_type(num_type(flex(TVAR1)), int_overflow)),
|
||||
Box::new(result_type(num_type(flex(TVAR1)), overflow)),
|
||||
),
|
||||
);
|
||||
|
||||
|
@ -275,8 +275,8 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
});
|
||||
|
||||
// addChecked or (+) : Num a, Num a -> Result (Num a) [ IntOverflow ]*
|
||||
let int_overflow = SolvedType::TagUnion(
|
||||
vec![(TagName::Global("IntOverflow".into()), vec![])],
|
||||
let overflow = SolvedType::TagUnion(
|
||||
vec![(TagName::Global("Overflow".into()), vec![])],
|
||||
Box::new(SolvedType::Wildcard),
|
||||
);
|
||||
|
||||
@ -284,7 +284,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||
let_tvars! { u, v, w, num, result, star };
|
||||
unique_function(
|
||||
vec![num_type(u, num), num_type(v, num)],
|
||||
result_type(result, num_type(w, num), lift(star, int_overflow)),
|
||||
result_type(result, num_type(w, num), lift(star, overflow)),
|
||||
)
|
||||
});
|
||||
|
||||
|
@ -258,7 +258,7 @@ fn num_add_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
//
|
||||
// if arg_3.b then
|
||||
// # overflow
|
||||
// Err IntOverflow
|
||||
// Err Overflow
|
||||
// else
|
||||
// # all is well
|
||||
// Ok arg_3.a
|
||||
@ -281,7 +281,7 @@ fn num_add_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||
// overflow!
|
||||
no_region(tag(
|
||||
"Err",
|
||||
vec![tag("DivByZero", Vec::new(), var_store)],
|
||||
vec![tag("Overflow", Vec::new(), var_store)],
|
||||
var_store,
|
||||
)),
|
||||
)],
|
||||
|
@ -990,8 +990,8 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||
}
|
||||
}
|
||||
|
||||
// TODO verify that this is required (better safe than sorry)
|
||||
if filler > 0 {
|
||||
// TODO verify that this is required (better safe than sorry)
|
||||
field_types.push(env.context.i8_type().array_type(filler).into());
|
||||
}
|
||||
|
||||
@ -1381,15 +1381,6 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||
|
||||
builder.build_conditional_branch(value, then_block, else_block);
|
||||
|
||||
// build then block
|
||||
builder.position_at_end(then_block);
|
||||
let then_val = build_exp_stmt(env, layout_ids, scope, parent, pass_stmt);
|
||||
if then_block.get_terminator().is_none() {
|
||||
builder.build_unconditional_branch(cont_block);
|
||||
let then_block = builder.get_insert_block().unwrap();
|
||||
blocks.push((&then_val, then_block));
|
||||
}
|
||||
|
||||
// build else block
|
||||
builder.position_at_end(else_block);
|
||||
let else_val = build_exp_stmt(env, layout_ids, scope, parent, fail_stmt);
|
||||
@ -1399,6 +1390,15 @@ pub fn build_exp_stmt<'a, 'ctx, 'env>(
|
||||
blocks.push((&else_val, else_block));
|
||||
}
|
||||
|
||||
// build then block
|
||||
builder.position_at_end(then_block);
|
||||
let then_val = build_exp_stmt(env, layout_ids, scope, parent, pass_stmt);
|
||||
if then_block.get_terminator().is_none() {
|
||||
builder.build_unconditional_branch(cont_block);
|
||||
let then_block = builder.get_insert_block().unwrap();
|
||||
blocks.push((&then_val, then_block));
|
||||
}
|
||||
|
||||
// emit merge block
|
||||
if blocks.is_empty() {
|
||||
// SAFETY there are no other references to this block in this case
|
||||
@ -2115,7 +2115,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
list_join(env, inplace, parent, list, outer_list_layout)
|
||||
}
|
||||
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling | NumFloor
|
||||
| NumToFloat => {
|
||||
| NumToFloat | NumIsFinite => {
|
||||
debug_assert_eq!(args.len(), 1);
|
||||
|
||||
let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||
@ -2250,6 +2250,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||
),
|
||||
Float128 | Float64 | Float32 | Float16 => build_float_binop(
|
||||
env,
|
||||
parent,
|
||||
lhs_arg.into_float_value(),
|
||||
lhs_layout,
|
||||
rhs_arg.into_float_value(),
|
||||
@ -2504,6 +2505,7 @@ fn call_bitcode_fn<'a, 'ctx, 'env>(
|
||||
|
||||
fn build_float_binop<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
lhs: FloatValue<'ctx>,
|
||||
_lhs_layout: &Layout<'a>,
|
||||
rhs: FloatValue<'ctx>,
|
||||
@ -2516,9 +2518,60 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||
let bd = env.builder;
|
||||
|
||||
match op {
|
||||
NumAdd => bd.build_float_add(lhs, rhs, "add_float").into(),
|
||||
NumAdd => {
|
||||
let builder = env.builder;
|
||||
let context = env.context;
|
||||
|
||||
let result = bd.build_float_add(lhs, rhs, "add_float");
|
||||
|
||||
let is_finite =
|
||||
call_bitcode_fn(NumIsFinite, env, &[result.into()], "is_finite_").into_int_value();
|
||||
|
||||
let then_block = context.append_basic_block(parent, "then_block");
|
||||
let throw_block = context.append_basic_block(parent, "throw_block");
|
||||
|
||||
builder.build_conditional_branch(is_finite, then_block, throw_block);
|
||||
|
||||
builder.position_at_end(throw_block);
|
||||
|
||||
throw_exception(env, "float addition overflowed!");
|
||||
|
||||
builder.position_at_end(then_block);
|
||||
|
||||
result.into()
|
||||
}
|
||||
NumAddChecked => {
|
||||
let builder = env.builder;
|
||||
let context = env.context;
|
||||
|
||||
let result = bd.build_float_add(lhs, rhs, "add_float");
|
||||
|
||||
let is_finite =
|
||||
call_bitcode_fn(NumIsFinite, env, &[result.into()], "is_finite_").into_int_value();
|
||||
let is_infinite = bd.build_not(is_finite, "negate");
|
||||
|
||||
let struct_type = context.struct_type(
|
||||
&[context.f64_type().into(), context.bool_type().into()],
|
||||
false,
|
||||
);
|
||||
|
||||
let struct_value = {
|
||||
let v1 = struct_type.const_zero();
|
||||
|
||||
let v2 = builder
|
||||
.build_insert_value(v1, result, 0, "set_result")
|
||||
.unwrap();
|
||||
|
||||
let v3 = builder
|
||||
.build_insert_value(v2, is_infinite, 1, "set_is_infinite")
|
||||
.unwrap();
|
||||
|
||||
v3.into_struct_value()
|
||||
};
|
||||
|
||||
struct_value.into()
|
||||
}
|
||||
NumAddWrap => unreachable!("wrapping addition is not defined on floats"),
|
||||
NumAddChecked => bd.build_float_add(lhs, rhs, "add_float_checked").into(),
|
||||
NumSub => bd.build_float_sub(lhs, rhs, "sub_float").into(),
|
||||
NumMul => bd.build_float_mul(lhs, rhs, "mul_float").into(),
|
||||
NumGt => bd.build_float_compare(OGT, lhs, rhs, "float_gt").into(),
|
||||
@ -2622,6 +2675,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||
env.context.i64_type(),
|
||||
"num_floor",
|
||||
),
|
||||
NumIsFinite => call_bitcode_fn(NumIsFinite, env, &[arg.into()], "is_finite_"),
|
||||
_ => {
|
||||
unreachable!("Unrecognized int unary operation: {:?}", op);
|
||||
}
|
||||
@ -2632,7 +2686,6 @@ fn define_global_str<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
message: &str,
|
||||
) -> inkwell::values::GlobalValue<'ctx> {
|
||||
let context = env.context;
|
||||
let module = env.module;
|
||||
|
||||
// hash the name so we don't re-define existing messages
|
||||
@ -2644,29 +2697,12 @@ fn define_global_str<'a, 'ctx, 'env>(
|
||||
message.hash(&mut hasher);
|
||||
let hash = hasher.finish();
|
||||
|
||||
format!("message_{}", hash)
|
||||
format!("_Error_message_{}", hash)
|
||||
};
|
||||
|
||||
match module.get_global(&name) {
|
||||
Some(current) => current,
|
||||
None => {
|
||||
let i8_type = context.i8_type();
|
||||
|
||||
// define the error message as a global constant
|
||||
let message_global =
|
||||
module.add_global(i8_type.array_type(message.len() as u32), None, &name);
|
||||
|
||||
let mut message_bytes = Vec::with_capacity_in(message.len(), env.arena);
|
||||
|
||||
for c in message.chars() {
|
||||
message_bytes.push(i8_type.const_int(c as u64, false));
|
||||
}
|
||||
|
||||
let const_array = i8_type.const_array(&message_bytes);
|
||||
message_global.set_initializer(&const_array);
|
||||
|
||||
message_global
|
||||
}
|
||||
None => unsafe { env.builder.build_global_string(message, name.as_str()) },
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -701,34 +701,34 @@ mod gen_num {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_checked() {
|
||||
fn int_add_checked() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.addChecked 1 2 is
|
||||
Ok 3 -> True
|
||||
_ -> False
|
||||
Ok v -> v
|
||||
_ -> -1
|
||||
"#
|
||||
),
|
||||
true,
|
||||
bool
|
||||
3,
|
||||
i64
|
||||
);
|
||||
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.addChecked 9_223_372_036_854_775_807 1 is
|
||||
Err IntOverflow -> True
|
||||
_ -> False
|
||||
Err Overflow -> -1
|
||||
Ok v -> v
|
||||
"#
|
||||
),
|
||||
true,
|
||||
bool
|
||||
-1,
|
||||
i64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn add_wrap() {
|
||||
fn int_add_wrap() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
@ -741,11 +741,42 @@ mod gen_num {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_add_checked_pass() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.addChecked 1.0 0.0 is
|
||||
Ok v -> v
|
||||
Err Overflow -> -1.0
|
||||
"#
|
||||
),
|
||||
1.0,
|
||||
f64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn float_add_checked_fail() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
when Num.addChecked 1.7976931348623157e308 1.7976931348623157e308 is
|
||||
Err Overflow -> -1
|
||||
Ok v -> v
|
||||
"#
|
||||
),
|
||||
-1.0,
|
||||
f64
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = r#"Roc failed with message: "float addition overflowed!"#)]
|
||||
fn float_overflow() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
1.7976931348623157E+308 + 1
|
||||
1.7976931348623157e308 + 1.7976931348623157e308
|
||||
"#
|
||||
),
|
||||
0.0,
|
||||
|
@ -42,6 +42,7 @@ pub enum LowLevel {
|
||||
NumCeiling,
|
||||
NumPowInt,
|
||||
NumFloor,
|
||||
NumIsFinite,
|
||||
Eq,
|
||||
NotEq,
|
||||
And,
|
||||
|
@ -526,6 +526,6 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||
| NumPowInt => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||
|
||||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
|
||||
| NumToFloat | Not => arena.alloc_slice_copy(&[irrelevant]),
|
||||
| NumToFloat | Not | NumIsFinite => arena.alloc_slice_copy(&[irrelevant]),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user