adds Num.subChecked

This commit is contained in:
Celso Bonutti Filho 2020-12-22 23:27:16 -03:00
parent 5d2abc2bd2
commit fe588c120c
8 changed files with 210 additions and 15 deletions

View file

@ -75,7 +75,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
), ),
); );
// addChecked : Num a, Num a -> Result (Num a) [ IntOverflow ]* // addChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
let overflow = SolvedType::TagUnion( let overflow = SolvedType::TagUnion(
vec![(TagName::Global("Overflow".into()), vec![])], vec![(TagName::Global("Overflow".into()), vec![])],
Box::new(SolvedType::Wildcard), Box::new(SolvedType::Wildcard),
@ -85,7 +85,7 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Symbol::NUM_ADD_CHECKED, Symbol::NUM_ADD_CHECKED,
top_level_function( top_level_function(
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))], vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
Box::new(result_type(num_type(flex(TVAR1)), overflow)), Box::new(result_type(num_type(flex(TVAR1)), overflow.clone())),
), ),
); );
@ -110,6 +110,15 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
top_level_function(vec![int_type(), int_type()], Box::new(int_type())), top_level_function(vec![int_type(), int_type()], Box::new(int_type())),
); );
// subChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
add_type(
Symbol::NUM_SUB_CHECKED,
top_level_function(
vec![num_type(flex(TVAR1)), num_type(flex(TVAR1))],
Box::new(result_type(num_type(flex(TVAR1)), overflow)),
),
);
// mul or (*) : Num a, Num a -> Num a // mul or (*) : Num a, Num a -> Num a
add_type( add_type(
Symbol::NUM_MUL, Symbol::NUM_MUL,

View file

@ -148,17 +148,19 @@ 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 ]* fn overflow() -> SolvedType {
let overflow = SolvedType::TagUnion( SolvedType::TagUnion(
vec![(TagName::Global("Overflow".into()), vec![])], vec![(TagName::Global("Overflow".into()), vec![])],
Box::new(SolvedType::Wildcard), Box::new(SolvedType::Wildcard),
); )
}
// addChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
add_type(Symbol::NUM_ADD_CHECKED, { add_type(Symbol::NUM_ADD_CHECKED, {
let_tvars! { u, v, w, num, result, star }; let_tvars! { u, v, w, num, result, star };
unique_function( unique_function(
vec![num_type(u, num), num_type(v, num)], vec![num_type(u, num), num_type(v, num)],
result_type(result, num_type(w, num), lift(star, overflow)), result_type(result, num_type(w, num), lift(star, overflow())),
) )
}); });
@ -180,6 +182,15 @@ 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))
}); });
// subChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
add_type(Symbol::NUM_SUB_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())),
)
});
// mul or (*) : Num a, Num a -> Num a // mul or (*) : Num a, Num a -> Num a
add_type(Symbol::NUM_MUL, { add_type(Symbol::NUM_MUL, {
let_tvars! { u, v, w, num }; let_tvars! { u, v, w, num };

View file

@ -83,6 +83,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
NUM_ADD_WRAP => num_add_wrap, NUM_ADD_WRAP => num_add_wrap,
NUM_SUB => num_sub, NUM_SUB => num_sub,
NUM_SUB_WRAP => num_sub_wrap, NUM_SUB_WRAP => num_sub_wrap,
NUM_SUB_CHECKED => num_sub_checked,
NUM_MUL => num_mul, NUM_MUL => num_mul,
NUM_GT => num_gt, NUM_GT => num_gt,
NUM_GTE => num_gte, NUM_GTE => num_gte,
@ -374,12 +375,12 @@ 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 /// Num.addWrap : Int, Int -> Int
fn num_add_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_add_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_binop(symbol, var_store, LowLevel::NumAddWrap) num_binop(symbol, var_store, LowLevel::NumAddWrap)
} }
/// Num.add : Num a, Num a -> Num a /// Num.addChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
fn num_add_checked(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_add_checked(symbol: Symbol, var_store: &mut VarStore) -> Def {
let bool_var = var_store.fresh(); let bool_var = var_store.fresh();
let num_var_1 = var_store.fresh(); let num_var_1 = var_store.fresh();
@ -473,11 +474,100 @@ fn num_sub(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_binop(symbol, var_store, LowLevel::NumSub) num_binop(symbol, var_store, LowLevel::NumSub)
} }
/// Num.subWrap : Num a, Num a -> Num a /// Num.subWrap : Int, Int -> Int
fn num_sub_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_sub_wrap(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_binop(symbol, var_store, LowLevel::NumSubWrap) num_binop(symbol, var_store, LowLevel::NumSubWrap)
} }
/// Num.subChecked : Num a, Num a -> Result (Num a) [ Overflow ]*
fn num_sub_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 NumSubChecked 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 NumSubChecked 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::NumSubChecked,
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);
defn(
symbol,
vec![(num_var_1, Symbol::ARG_1), (num_var_2, Symbol::ARG_2)],
var_store,
body,
ret_var,
)
}
/// Num.mul : Num a, Num a -> Num a /// Num.mul : Num a, Num a -> Num a
fn num_mul(symbol: Symbol, var_store: &mut VarStore) -> Def { fn num_mul(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_binop(symbol, var_store, LowLevel::NumMul) num_binop(symbol, var_store, LowLevel::NumMul)

View file

@ -3002,7 +3002,8 @@ fn run_low_level<'a, 'ctx, 'env>(
} }
NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked NumAdd | NumSub | NumMul | NumLt | NumLte | NumGt | NumGte | NumRemUnchecked
| NumAddWrap | NumAddChecked | NumDivUnchecked | NumPow | NumPowInt | NumSubWrap => { | NumAddWrap | NumAddChecked | NumDivUnchecked | NumPow | NumPowInt | NumSubWrap
| NumSubChecked => {
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]);
@ -3257,6 +3258,7 @@ fn build_int_binop<'a, 'ctx, 'env>(
sub_result sub_result
} }
NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(), NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(),
NumSubChecked => env.call_intrinsic(LLVM_SSUB_WITH_OVERFLOW_I64, &[lhs.into(), rhs.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(),
NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(), NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(),
@ -3446,6 +3448,32 @@ fn build_float_binop<'a, 'ctx, 'env>(
result.into() result.into()
} }
NumSubChecked => {
let context = env.context;
let result = bd.build_float_sub(lhs, rhs, "sub_float");
let is_finite =
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_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()
}
NumSubWrap => unreachable!("wrapping subtraction is not defined on floats"), NumSubWrap => unreachable!("wrapping subtraction is not defined on floats"),
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(),

View file

@ -784,4 +784,57 @@ mod gen_num {
f64 f64
); );
} }
#[test]
fn int_sub_checked() {
assert_evals_to!(
indoc!(
r#"
when Num.subChecked 1 2 is
Ok v -> v
_ -> -1
"#
),
-1,
i64
);
assert_evals_to!(
indoc!(
r#"
when Num.subChecked Num.minInt 1 is
Err Overflow -> -1
Ok v -> v
"#
),
-1,
i64
);
}
fn float_sub_checked() {
assert_evals_to!(
indoc!(
r#"
when Num.subChecked 1.0 0.0 is
Ok v -> v
Err Overflow -> -1.0
"#
),
1.0,
f64
);
assert_evals_to!(
indoc!(
r#"
when Num.subChecked -1.7976931348623157e308 1.7976931348623157e308 is
Err Overflow -> -1
Ok v -> v
"#
),
-1.0,
f64
);
}
} }

View file

@ -32,6 +32,7 @@ pub enum LowLevel {
NumAddChecked, NumAddChecked,
NumSub, NumSub,
NumSubWrap, NumSubWrap,
NumSubChecked,
NumMul, NumMul,
NumGt, NumGt,
NumGte, NumGte,

View file

@ -799,6 +799,7 @@ define_builtins! {
56 NUM_ASIN: "asin" 56 NUM_ASIN: "asin"
57 NUM_BITWISE_AND: "bitwiseAnd" 57 NUM_BITWISE_AND: "bitwiseAnd"
58 NUM_SUB_WRAP: "subWrap" 58 NUM_SUB_WRAP: "subWrap"
59 NUM_SUB_CHECKED: "subChecked"
} }
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

@ -539,9 +539,11 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
ListWalkBackwards => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]), ListWalkBackwards => arena.alloc_slice_copy(&[borrowed, irrelevant, owned]),
ListSum => arena.alloc_slice_copy(&[borrowed]), ListSum => arena.alloc_slice_copy(&[borrowed]),
Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap | NumMul | NumGt Eq | NotEq | And | Or | NumAdd | NumAddWrap | NumAddChecked | NumSub | NumSubWrap
| NumGte | NumLt | NumLte | NumCompare | NumDivUnchecked | NumRemUnchecked | NumPow | NumSubChecked | NumMul | NumGt | NumGte | NumLt | NumLte | NumCompare
| NumPowInt | NumBitwiseAnd => arena.alloc_slice_copy(&[irrelevant, irrelevant]), | NumDivUnchecked | NumRemUnchecked | NumPow | NumPowInt | NumBitwiseAnd => {
arena.alloc_slice_copy(&[irrelevant, irrelevant])
}
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumRound | NumCeiling | NumFloor
| NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin => { | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin => {