mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 00:24:34 +00:00
crash upon integer overflow
This commit is contained in:
parent
16fc5dd497
commit
f732eb3e83
7 changed files with 107 additions and 6 deletions
|
@ -187,6 +187,26 @@ 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![])],
|
||||||
|
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)), int_overflow)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// addWrap or (+) : 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 or (+) : Num a, Num a -> Result (Num a) [ IntOverflow ]*
|
||||||
|
let int_overflow = SolvedType::TagUnion(
|
||||||
|
vec![(TagName::Global("IntOverflow".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, int_overflow)),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
// addWrap or (+) : 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 };
|
||||||
|
|
|
@ -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>,
|
||||||
|
@ -2219,7 +2226,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 +2241,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(),
|
||||||
|
@ -2413,6 +2421,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 +2434,40 @@ 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 => {
|
||||||
|
// TODO
|
||||||
|
let builder = env.builder;
|
||||||
|
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 = builder.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");
|
||||||
|
|
||||||
|
builder.build_conditional_branch(condition, then_block, throw_block);
|
||||||
|
|
||||||
|
builder.position_at_end(throw_block);
|
||||||
|
|
||||||
|
throw_exception(env, "integer addition overflowed!");
|
||||||
|
|
||||||
|
builder.position_at_end(then_block);
|
||||||
|
|
||||||
|
add_result
|
||||||
|
}
|
||||||
|
NumAddWrap => bd.build_int_add(lhs, rhs, "add_int_wrap").into(),
|
||||||
|
NumAddChecked => bd.build_int_add(lhs, rhs, "add_int_checked").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(),
|
||||||
|
@ -2475,6 +2517,8 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
NumAdd => bd.build_float_add(lhs, rhs, "add_float").into(),
|
NumAdd => bd.build_float_add(lhs, rhs, "add_float").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(),
|
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(),
|
||||||
|
|
|
@ -685,4 +685,18 @@ 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
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ pub enum LowLevel {
|
||||||
ListKeepIf,
|
ListKeepIf,
|
||||||
ListWalkRight,
|
ListWalkRight,
|
||||||
NumAdd,
|
NumAdd,
|
||||||
|
NumAddWrap,
|
||||||
|
NumAddChecked,
|
||||||
NumSub,
|
NumSub,
|
||||||
NumMul,
|
NumMul,
|
||||||
NumGt,
|
NumGt,
|
||||||
|
|
|
@ -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,10 +521,9 @@ 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 => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue