mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-01 15:51:12 +00:00
Merge pull request #539 from rtfeldman/num-add-wrap-check
Addition variants
This commit is contained in:
commit
cd5e2b6817
12 changed files with 360 additions and 39 deletions
|
@ -36,7 +36,7 @@ $ brew install https://raw.githubusercontent.com/Homebrew/homebrew-core/6616d50f
|
||||||
$ brew pin llvm
|
$ brew pin llvm
|
||||||
```
|
```
|
||||||
|
|
||||||
If that doesn't work and you get a `brew` error `Error: Calling Installation of llvm from a GitHub commit URL is disabled! Use 'brew extract llvm' to stable tap on GitHub instead.` while trying the above solution, you can follow the steps extracting the formular into your private tap (one public version is at `sladwig/tap/llvm`). If installing LLVM still fails, it might help to run `sudo xcode-select -r` before installing again.
|
If that doesn't work and you get a `brew` error `Error: Calling Installation of llvm from a GitHub commit URL is disabled! Use 'brew extract llvm' to stable tap on GitHub instead.` while trying the above solution, you can follow the steps extracting the formula into your private tap (one public version is at `sladwig/tap/llvm`). If installing LLVM still fails, it might help to run `sudo xcode-select -r` before installing again.
|
||||||
|
|
||||||
### LLVM installation on Windows
|
### LLVM installation on Windows
|
||||||
|
|
||||||
|
|
|
@ -210,6 +210,12 @@ fn build_file(
|
||||||
"host.rs",
|
"host.rs",
|
||||||
"-o",
|
"-o",
|
||||||
binary_path.as_path().to_str().unwrap(),
|
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)
|
.current_dir(cwd)
|
||||||
.spawn()
|
.spawn()
|
||||||
|
|
|
@ -36,3 +36,12 @@ pub fn pow_int_(mut base: i64, mut exp: i64) -> i64 {
|
||||||
|
|
||||||
acc
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -187,6 +187,26 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// addChecked : Num a, Num a -> Result (Num a) [ IntOverflow ]*
|
||||||
|
let overflow = SolvedType::TagUnion(
|
||||||
|
vec![(TagName::Global("Overflow".into()), vec![])],
|
||||||
|
Box::new(SolvedType::Wildcard),
|
||||||
|
);
|
||||||
|
|
||||||
|
add_type(
|
||||||
|
Symbol::NUM_ADD_CHECKED,
|
||||||
|
SolvedType::Func(
|
||||||
|
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(num_type(flex(TVAR1)), overflow)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// addWrap : Int, Int -> Int
|
||||||
|
add_type(
|
||||||
|
Symbol::NUM_ADD_WRAP,
|
||||||
|
SolvedType::Func(vec![int_type(), int_type()], Box::new(int_type())),
|
||||||
|
);
|
||||||
|
|
||||||
// sub or (-) : Num a, Num a -> Num a
|
// sub or (-) : Num a, Num a -> Num a
|
||||||
add_type(
|
add_type(
|
||||||
Symbol::NUM_SUB,
|
Symbol::NUM_SUB,
|
||||||
|
|
|
@ -274,6 +274,26 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
unique_function(vec![num_type(u, num), num_type(v, num)], num_type(w, num))
|
unique_function(vec![num_type(u, num), num_type(v, num)], num_type(w, num))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// addChecked : Num a, Num a -> Result (Num a) [ IntOverflow ]*
|
||||||
|
let overflow = SolvedType::TagUnion(
|
||||||
|
vec![(TagName::Global("Overflow".into()), vec![])],
|
||||||
|
Box::new(SolvedType::Wildcard),
|
||||||
|
);
|
||||||
|
|
||||||
|
add_type(Symbol::NUM_ADD_CHECKED, {
|
||||||
|
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, overflow)),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// addWrap : Int, Int -> Int
|
||||||
|
add_type(Symbol::NUM_ADD_WRAP, {
|
||||||
|
let_tvars! { u, v, w };
|
||||||
|
unique_function(vec![int_type(u), int_type(v)], int_type(w))
|
||||||
|
});
|
||||||
|
|
||||||
// sub or (-) : Num a, Num a -> Num a
|
// sub or (-) : Num a, Num a -> Num a
|
||||||
add_type(Symbol::NUM_SUB, {
|
add_type(Symbol::NUM_SUB, {
|
||||||
let_tvars! { u, v, w, num };
|
let_tvars! { u, v, w, num };
|
||||||
|
|
|
@ -68,6 +68,8 @@ pub fn builtin_defs(var_store: &mut VarStore) -> MutMap<Symbol, Def> {
|
||||||
Symbol::LIST_KEEP_IF => list_keep_if,
|
Symbol::LIST_KEEP_IF => list_keep_if,
|
||||||
Symbol::LIST_WALK_RIGHT => list_walk_right,
|
Symbol::LIST_WALK_RIGHT => list_walk_right,
|
||||||
Symbol::NUM_ADD => num_add,
|
Symbol::NUM_ADD => num_add,
|
||||||
|
Symbol::NUM_ADD_CHECKED => num_add_checked,
|
||||||
|
Symbol::NUM_ADD_WRAP => num_add_wrap,
|
||||||
Symbol::NUM_SUB => num_sub,
|
Symbol::NUM_SUB => num_sub,
|
||||||
Symbol::NUM_MUL => num_mul,
|
Symbol::NUM_MUL => num_mul,
|
||||||
Symbol::NUM_GT => num_gt,
|
Symbol::NUM_GT => num_gt,
|
||||||
|
@ -238,6 +240,105 @@ fn num_add(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
num_binop(symbol, var_store, LowLevel::NumAdd)
|
num_binop(symbol, var_store, LowLevel::NumAdd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Num.add : Num a, Num a -> Num a
|
||||||
|
fn num_add_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
num_binop(symbol, var_store, LowLevel::NumAddWrap)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Num.add : Num a, Num a -> Num a
|
||||||
|
fn num_add_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
let bool_var = var_store.fresh();
|
||||||
|
let num_var_1 = var_store.fresh();
|
||||||
|
let num_var_2 = var_store.fresh();
|
||||||
|
let num_var_3 = var_store.fresh();
|
||||||
|
let ret_var = var_store.fresh();
|
||||||
|
let record_var = var_store.fresh();
|
||||||
|
|
||||||
|
// let arg_3 = RunLowLevel NumAddChecked arg_1 arg_2
|
||||||
|
//
|
||||||
|
// if arg_3.b then
|
||||||
|
// # overflow
|
||||||
|
// Err Overflow
|
||||||
|
// else
|
||||||
|
// # all is well
|
||||||
|
// Ok arg_3.a
|
||||||
|
|
||||||
|
let cont = If {
|
||||||
|
branch_var: ret_var,
|
||||||
|
cond_var: bool_var,
|
||||||
|
branches: vec![(
|
||||||
|
// if-condition
|
||||||
|
no_region(
|
||||||
|
// arg_3.b
|
||||||
|
Access {
|
||||||
|
record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "b".into(),
|
||||||
|
field_var: var_store.fresh(),
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_3))),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// overflow!
|
||||||
|
no_region(tag(
|
||||||
|
"Err",
|
||||||
|
vec![tag("Overflow", Vec::new(), var_store)],
|
||||||
|
var_store,
|
||||||
|
)),
|
||||||
|
)],
|
||||||
|
final_else: Box::new(
|
||||||
|
// all is well
|
||||||
|
no_region(
|
||||||
|
// Ok arg_3.a
|
||||||
|
tag(
|
||||||
|
"Ok",
|
||||||
|
vec![
|
||||||
|
// arg_3.a
|
||||||
|
Access {
|
||||||
|
record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "a".into(),
|
||||||
|
field_var: num_var_3,
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_3))),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
var_store,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
// arg_3 = RunLowLevel NumAddChecked arg_1 arg_2
|
||||||
|
let def = crate::def::Def {
|
||||||
|
loc_pattern: no_region(Pattern::Identifier(Symbol::ARG_3)),
|
||||||
|
loc_expr: no_region(RunLowLevel {
|
||||||
|
op: LowLevel::NumAddChecked,
|
||||||
|
args: vec![
|
||||||
|
(num_var_1, Var(Symbol::ARG_1)),
|
||||||
|
(num_var_2, Var(Symbol::ARG_2)),
|
||||||
|
],
|
||||||
|
ret_var: record_var,
|
||||||
|
}),
|
||||||
|
expr_var: record_var,
|
||||||
|
pattern_vars: SendMap::default(),
|
||||||
|
annotation: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = LetNonRec(
|
||||||
|
Box::new(def),
|
||||||
|
Box::new(no_region(cont)),
|
||||||
|
ret_var,
|
||||||
|
SendMap::default(),
|
||||||
|
);
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(num_var_1, Symbol::ARG_1), (num_var_2, Symbol::ARG_2)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
ret_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Num.sub : Num a, Num a -> Num a
|
/// Num.sub : Num a, Num a -> Num a
|
||||||
fn num_sub(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn num_sub(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
num_binop(symbol, var_store, LowLevel::NumSub)
|
num_binop(symbol, var_store, LowLevel::NumSub)
|
||||||
|
|
|
@ -270,6 +270,12 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
|
||||||
LLVM_FLOOR_F64,
|
LLVM_FLOOR_F64,
|
||||||
f64_type.fn_type(&[f64_type.into()], false),
|
f64_type.fn_type(&[f64_type.into()], false),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
add_intrinsic(module, LLVM_SADD_WITH_OVERFLOW_I64, {
|
||||||
|
let fields = [i64_type.into(), i1_type.into()];
|
||||||
|
ctx.struct_type(&fields, false)
|
||||||
|
.fn_type(&[i64_type.into(), i64_type.into()], false)
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
|
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
|
||||||
|
@ -282,6 +288,7 @@ static LLVM_COS_F64: &str = "llvm.cos.f64";
|
||||||
static LLVM_POW_F64: &str = "llvm.pow.f64";
|
static LLVM_POW_F64: &str = "llvm.pow.f64";
|
||||||
static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
|
static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
|
||||||
static LLVM_FLOOR_F64: &str = "llvm.floor.f64";
|
static LLVM_FLOOR_F64: &str = "llvm.floor.f64";
|
||||||
|
static LLVM_SADD_WITH_OVERFLOW_I64: &str = "llvm.sadd.with.overflow.i64";
|
||||||
|
|
||||||
fn add_intrinsic<'ctx>(
|
fn add_intrinsic<'ctx>(
|
||||||
module: &Module<'ctx>,
|
module: &Module<'ctx>,
|
||||||
|
@ -940,8 +947,6 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
debug_assert!(*union_size > 1);
|
debug_assert!(*union_size > 1);
|
||||||
let ptr_size = env.ptr_bytes;
|
let ptr_size = env.ptr_bytes;
|
||||||
|
|
||||||
let mut filler = tag_layout.stack_size(ptr_size);
|
|
||||||
|
|
||||||
let ctx = env.context;
|
let ctx = env.context;
|
||||||
let builder = env.builder;
|
let builder = env.builder;
|
||||||
|
|
||||||
|
@ -978,16 +983,9 @@ pub fn build_exp_expr<'a, 'ctx, 'env>(
|
||||||
} else {
|
} else {
|
||||||
field_vals.push(val);
|
field_vals.push(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
filler -= field_size;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO verify that this is required (better safe than sorry)
|
|
||||||
if filler > 0 {
|
|
||||||
field_types.push(env.context.i8_type().array_type(filler).into());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create the struct_type
|
// Create the struct_type
|
||||||
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
let struct_type = ctx.struct_type(field_types.into_bump_slice(), false);
|
||||||
let mut struct_val = struct_type.const_zero().into();
|
let mut struct_val = struct_type.const_zero().into();
|
||||||
|
@ -2108,7 +2106,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
list_join(env, inplace, parent, list, outer_list_layout)
|
list_join(env, inplace, parent, list, outer_list_layout)
|
||||||
}
|
}
|
||||||
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling | NumFloor
|
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumSin | NumCos | NumCeiling | NumFloor
|
||||||
| NumToFloat => {
|
| NumToFloat | NumIsFinite => {
|
||||||
debug_assert_eq!(args.len(), 1);
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
||||||
let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
let (arg, arg_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||||
|
@ -2219,7 +2217,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
|
|
||||||
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
|
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
|
||||||
| NumDivUnchecked | NumPow | NumPowInt => {
|
| NumAddWrap | NumAddChecked | NumDivUnchecked | NumPow | NumPowInt => {
|
||||||
debug_assert_eq!(args.len(), 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
let (lhs_arg, lhs_layout) = load_symbol_and_layout(env, scope, &args[0]);
|
||||||
|
@ -2234,6 +2232,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
match lhs_builtin {
|
match lhs_builtin {
|
||||||
Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop(
|
Int128 | Int64 | Int32 | Int16 | Int8 => build_int_binop(
|
||||||
env,
|
env,
|
||||||
|
parent,
|
||||||
lhs_arg.into_int_value(),
|
lhs_arg.into_int_value(),
|
||||||
lhs_layout,
|
lhs_layout,
|
||||||
rhs_arg.into_int_value(),
|
rhs_arg.into_int_value(),
|
||||||
|
@ -2242,6 +2241,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
),
|
),
|
||||||
Float128 | Float64 | Float32 | Float16 => build_float_binop(
|
Float128 | Float64 | Float32 | Float16 => build_float_binop(
|
||||||
env,
|
env,
|
||||||
|
parent,
|
||||||
lhs_arg.into_float_value(),
|
lhs_arg.into_float_value(),
|
||||||
lhs_layout,
|
lhs_layout,
|
||||||
rhs_arg.into_float_value(),
|
rhs_arg.into_float_value(),
|
||||||
|
@ -2413,6 +2413,7 @@ where
|
||||||
|
|
||||||
fn build_int_binop<'a, 'ctx, 'env>(
|
fn build_int_binop<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
lhs: IntValue<'ctx>,
|
lhs: IntValue<'ctx>,
|
||||||
_lhs_layout: &Layout<'a>,
|
_lhs_layout: &Layout<'a>,
|
||||||
rhs: IntValue<'ctx>,
|
rhs: IntValue<'ctx>,
|
||||||
|
@ -2425,7 +2426,37 @@ fn build_int_binop<'a, 'ctx, 'env>(
|
||||||
let bd = env.builder;
|
let bd = env.builder;
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
NumAdd => bd.build_int_add(lhs, rhs, "add_int").into(),
|
NumAdd => {
|
||||||
|
let context = env.context;
|
||||||
|
let result = env
|
||||||
|
.call_intrinsic(LLVM_SADD_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()])
|
||||||
|
.into_struct_value();
|
||||||
|
|
||||||
|
let add_result = bd.build_extract_value(result, 0, "add_result").unwrap();
|
||||||
|
let has_overflowed = bd.build_extract_value(result, 1, "has_overflowed").unwrap();
|
||||||
|
|
||||||
|
let condition = bd.build_int_compare(
|
||||||
|
IntPredicate::EQ,
|
||||||
|
has_overflowed.into_int_value(),
|
||||||
|
context.bool_type().const_zero(),
|
||||||
|
"has_not_overflowed",
|
||||||
|
);
|
||||||
|
|
||||||
|
let then_block = context.append_basic_block(parent, "then_block");
|
||||||
|
let throw_block = context.append_basic_block(parent, "throw_block");
|
||||||
|
|
||||||
|
bd.build_conditional_branch(condition, then_block, throw_block);
|
||||||
|
|
||||||
|
bd.position_at_end(throw_block);
|
||||||
|
|
||||||
|
throw_exception(env, "integer addition overflowed!");
|
||||||
|
|
||||||
|
bd.position_at_end(then_block);
|
||||||
|
|
||||||
|
add_result
|
||||||
|
}
|
||||||
|
NumAddWrap => bd.build_int_add(lhs, rhs, "add_int_wrap").into(),
|
||||||
|
NumAddChecked => env.call_intrinsic(LLVM_SADD_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]),
|
||||||
NumSub => bd.build_int_sub(lhs, rhs, "sub_int").into(),
|
NumSub => bd.build_int_sub(lhs, rhs, "sub_int").into(),
|
||||||
NumMul => bd.build_int_mul(lhs, rhs, "mul_int").into(),
|
NumMul => bd.build_int_mul(lhs, rhs, "mul_int").into(),
|
||||||
NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(),
|
NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(),
|
||||||
|
@ -2462,6 +2493,7 @@ fn call_bitcode_fn<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
fn build_float_binop<'a, 'ctx, 'env>(
|
fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
parent: FunctionValue<'ctx>,
|
||||||
lhs: FloatValue<'ctx>,
|
lhs: FloatValue<'ctx>,
|
||||||
_lhs_layout: &Layout<'a>,
|
_lhs_layout: &Layout<'a>,
|
||||||
rhs: FloatValue<'ctx>,
|
rhs: FloatValue<'ctx>,
|
||||||
|
@ -2474,7 +2506,55 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
let bd = env.builder;
|
let bd = env.builder;
|
||||||
|
|
||||||
match op {
|
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 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 = bd.build_insert_value(v1, result, 0, "set_result").unwrap();
|
||||||
|
let v3 = bd
|
||||||
|
.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"),
|
||||||
NumSub => bd.build_float_sub(lhs, rhs, "sub_float").into(),
|
NumSub => bd.build_float_sub(lhs, rhs, "sub_float").into(),
|
||||||
NumMul => bd.build_float_mul(lhs, rhs, "mul_float").into(),
|
NumMul => bd.build_float_mul(lhs, rhs, "mul_float").into(),
|
||||||
NumGt => bd.build_float_compare(OGT, lhs, rhs, "float_gt").into(),
|
NumGt => bd.build_float_compare(OGT, lhs, rhs, "float_gt").into(),
|
||||||
|
@ -2578,6 +2658,7 @@ fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||||
env.context.i64_type(),
|
env.context.i64_type(),
|
||||||
"num_floor",
|
"num_floor",
|
||||||
),
|
),
|
||||||
|
NumIsFinite => call_bitcode_fn(NumIsFinite, env, &[arg.into()], "is_finite_"),
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("Unrecognized int unary operation: {:?}", op);
|
unreachable!("Unrecognized int unary operation: {:?}", op);
|
||||||
}
|
}
|
||||||
|
@ -2588,7 +2669,6 @@ fn define_global_str<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
message: &str,
|
message: &str,
|
||||||
) -> inkwell::values::GlobalValue<'ctx> {
|
) -> inkwell::values::GlobalValue<'ctx> {
|
||||||
let context = env.context;
|
|
||||||
let module = env.module;
|
let module = env.module;
|
||||||
|
|
||||||
// hash the name so we don't re-define existing messages
|
// hash the name so we don't re-define existing messages
|
||||||
|
@ -2600,29 +2680,12 @@ fn define_global_str<'a, 'ctx, 'env>(
|
||||||
message.hash(&mut hasher);
|
message.hash(&mut hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
|
|
||||||
format!("message_{}", hash)
|
format!("_Error_message_{}", hash)
|
||||||
};
|
};
|
||||||
|
|
||||||
match module.get_global(&name) {
|
match module.get_global(&name) {
|
||||||
Some(current) => current,
|
Some(current) => current,
|
||||||
None => {
|
None => unsafe { env.builder.build_global_string(message, name.as_str()) },
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Binary file not shown.
|
@ -685,4 +685,102 @@ mod gen_num {
|
||||||
fn pow_int() {
|
fn pow_int() {
|
||||||
assert_evals_to!("Num.powInt 2 3", 8, i64);
|
assert_evals_to!("Num.powInt 2 3", 8, i64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[should_panic(expected = r#"Roc failed with message: "integer addition overflowed!"#)]
|
||||||
|
fn int_overflow() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
9_223_372_036_854_775_807 + 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn int_add_checked() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.addChecked 1 2 is
|
||||||
|
Ok v -> v
|
||||||
|
_ -> -1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
3,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
when Num.addChecked 9_223_372_036_854_775_807 1 is
|
||||||
|
Err Overflow -> -1
|
||||||
|
Ok v -> v
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
-1,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn int_add_wrap() {
|
||||||
|
assert_evals_to!(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
Num.addWrap 9_223_372_036_854_775_807 1
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
std::i64::MIN,
|
||||||
|
i64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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.7976931348623157e308 + 1.7976931348623157e308
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
0.0,
|
||||||
|
f64
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ pub enum LowLevel {
|
||||||
ListKeepIf,
|
ListKeepIf,
|
||||||
ListWalkRight,
|
ListWalkRight,
|
||||||
NumAdd,
|
NumAdd,
|
||||||
|
NumAddWrap,
|
||||||
|
NumAddChecked,
|
||||||
NumSub,
|
NumSub,
|
||||||
NumMul,
|
NumMul,
|
||||||
NumGt,
|
NumGt,
|
||||||
|
@ -40,6 +42,7 @@ pub enum LowLevel {
|
||||||
NumCeiling,
|
NumCeiling,
|
||||||
NumPowInt,
|
NumPowInt,
|
||||||
NumFloor,
|
NumFloor,
|
||||||
|
NumIsFinite,
|
||||||
Eq,
|
Eq,
|
||||||
NotEq,
|
NotEq,
|
||||||
And,
|
And,
|
||||||
|
|
|
@ -644,6 +644,8 @@ define_builtins! {
|
||||||
39 NUM_CEILING: "ceiling"
|
39 NUM_CEILING: "ceiling"
|
||||||
40 NUM_POW_INT: "powInt"
|
40 NUM_POW_INT: "powInt"
|
||||||
41 NUM_FLOOR: "floor"
|
41 NUM_FLOOR: "floor"
|
||||||
|
42 NUM_ADD_WRAP: "addWrap"
|
||||||
|
43 NUM_ADD_CHECKED: "addChecked"
|
||||||
}
|
}
|
||||||
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
|
||||||
|
|
|
@ -521,12 +521,11 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
ListKeepIf => arena.alloc_slice_copy(&[owned, irrelevant]),
|
ListKeepIf => arena.alloc_slice_copy(&[owned, irrelevant]),
|
||||||
ListWalkRight => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
ListWalkRight => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
|
||||||
|
|
||||||
Eq | NotEq | And | Or | NumAdd | NumSub | NumMul | NumGt | NumGte | NumLt | NumLte
|
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumMul | NumGt
|
||||||
| NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt => {
|
| NumGte | NumLt | NumLte | NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow
|
||||||
arena.alloc_slice_copy(&[irrelevant, irrelevant])
|
| NumPowInt => arena.alloc_slice_copy(&[irrelevant, irrelevant]),
|
||||||
}
|
|
||||||
|
|
||||||
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
|
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
|
||||||
| NumToFloat | Not => arena.alloc_slice_copy(&[irrelevant]),
|
| NumToFloat | Not | NumIsFinite => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue