Add toF32/64 and checked versions

This commit is contained in:
Richard Feldman 2022-04-06 22:40:58 -04:00
parent bd623d65bc
commit cd00a98636
No known key found for this signature in database
GPG key ID: 7E4127D1E4241798
11 changed files with 108 additions and 11 deletions

View file

@ -628,6 +628,15 @@ toU16 : Int * -> U16
toU32 : Int * -> U32
toU64 : Int * -> U64
toU128 : Int * -> U128
## Convert a [Num] to a [F32]. If the given number can't be precisely represented in a [F32],
## there will be a loss of precision.
toF32 : Num * -> F32
## Convert a [Num] to a [F64]. If the given number can't be precisely represented in a [F64],
## there will be a loss of precision.
toF64 : Num * -> F64
## Convert any [Int] to a specifically-sized [Int], after checking validity.
## These are checked bitwise operations,
## so if the source number is outside the target range, then these will
@ -643,6 +652,9 @@ toU32Checked : Int * -> Result U32 [ OutOfBounds ]*
toU64Checked : Int * -> Result U64 [ OutOfBounds ]*
toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
toF32Checked : Num * -> Result F32 [ OutOfBounds ]
toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
## Convert a number to a [Str].
##
## This is the same as calling `Num.format {}` - so for more details on
@ -765,14 +777,6 @@ toU32 : Int * -> U32
toU64 : Int * -> U64
toU128 : Int * -> U128
## Convert a #Num to a #F32. If the given number can't be precisely represented in a #F32,
## there will be a loss of precision.
toF32 : Num * -> F32
## Convert a #Num to a #F64. If the given number can't be precisely represented in a #F64,
## there will be a loss of precision.
toF64 : Num * -> F64
## Convert a #Num to a #Dec. If the given number can't be precisely represented in a #Dec,
## there will be a loss of precision.
toDec : Num * -> Dec

View file

@ -336,6 +336,9 @@ toU32 : Int * -> U32
toU64 : Int * -> U64
toU128 : Int * -> U128
toF32 : Num * -> F32
toF64 : Num * -> F64
toI8Checked : Int * -> Result I8 [ OutOfBounds ]*
toI16Checked : Int * -> Result I16 [ OutOfBounds ]*
toI32Checked : Int * -> Result I32 [ OutOfBounds ]*
@ -346,3 +349,5 @@ toU16Checked : Int * -> Result U16 [ OutOfBounds ]*
toU32Checked : Int * -> Result U32 [ OutOfBounds ]*
toU64Checked : Int * -> Result U64 [ OutOfBounds ]*
toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
toF32Checked : Num * -> Result F32 [ OutOfBounds ]*
toF64Checked : Num * -> Result F64 [ OutOfBounds ]*

View file

@ -615,7 +615,35 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
add_top_level_function_type!(
Symbol::NUM_TO_NAT_CHECKED,
vec![int_type(flex(TVAR1))],
Box::new(result_type(nat_type(), out_of_bounds)),
Box::new(result_type(nat_type(), out_of_bounds.clone())),
);
// toF32 : Num * -> F32
add_top_level_function_type!(
Symbol::NUM_TO_F32,
vec![num_type(flex(TVAR1))],
Box::new(f32_type()),
);
// toF32Checked : Num * -> Result F32 [ OutOfBounds ]*
add_top_level_function_type!(
Symbol::NUM_TO_F32_CHECKED,
vec![num_type(flex(TVAR1))],
Box::new(result_type(f32_type(), out_of_bounds.clone())),
);
// toF64 : Num * -> F64
add_top_level_function_type!(
Symbol::NUM_TO_F64,
vec![num_type(flex(TVAR1))],
Box::new(f64_type()),
);
// toF64Checked : Num * -> Result F64 [ OutOfBounds ]*
add_top_level_function_type!(
Symbol::NUM_TO_F64_CHECKED,
vec![num_type(flex(TVAR1))],
Box::new(result_type(f64_type(), out_of_bounds)),
);
// toStr : Num a -> Str

View file

@ -482,6 +482,18 @@ fn num_to_nat(symbol: Symbol, var_store: &mut VarStore) -> Def {
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
}
// Num.toF32 : Num * -> F32
fn num_to_f32(symbol: Symbol, var_store: &mut VarStore) -> Def {
// Defer to NumToFloatCast
lowlevel_1(symbol, LowLevel::NumToFloatCast, var_store)
}
// Num.toF64 : Num * -> F64
fn num_to_f64(symbol: Symbol, var_store: &mut VarStore) -> Def {
// Defer to NumToFloatCast
lowlevel_1(symbol, LowLevel::NumToFloatCast, var_store)
}
fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel) -> Def {
let bool_var = var_store.fresh();
let num_var_1 = var_store.fresh();
@ -589,6 +601,8 @@ num_to_checked! {
num_to_u64_checked
num_to_u128_checked
num_to_nat_checked
num_to_f32_checked
num_to_f64_checked
}
// Num.toStr : Num a -> Str

View file

@ -5865,6 +5865,23 @@ fn run_low_level<'a, 'ctx, 'env>(
.build_int_cast_sign_flag(arg, to, to_signed, "inc_cast")
.into()
}
NumToFloatCast => {
debug_assert_eq!(args.len(), 1);
let arg = load_symbol(scope, &args[0]).into_int_value();
todo!("TODO if it's a float, cast; if it's an int, do either signed_int_to_float or unsigned_int_to_float; if it's Dec, panic for now");
// let to = basic_type_from_layout(env, layout).into_int_type();
// let to_signed = intwidth_from_layout(*layout).is_signed();
// env.builder
// .build_int_cast_sign_flag(arg, to, to_signed, "inc_cast")
// .into()
}
NumToFloatChecked => {
// NOTE: For some reason there's no entry here for NumToIntChecked - why is that?
todo!("implement checked float conversion");
}
Eq => {
debug_assert_eq!(args.len(), 2);

View file

@ -677,9 +677,15 @@ impl<'a> LowLevelCall<'a> {
_ => todo!("{:?}: {:?} -> {:?}", self.lowlevel, arg_type, ret_type),
}
}
NumToFloatCast => {
todo!("implement toF32 and toF64");
}
NumToIntChecked => {
todo!()
}
NumToFloatChecked => {
todo!("implement toF32Checked and toF64Checked");
}
And => {
self.load_args(backend);
backend.code_builder.i32_and();

View file

@ -111,7 +111,9 @@ pub enum LowLevel {
NumShiftRightBy,
NumShiftRightZfBy,
NumIntCast,
NumToFloatCast,
NumToIntChecked,
NumToFloatChecked,
NumToStr,
Eq,
NotEq,

View file

@ -1053,6 +1053,10 @@ define_builtins! {
144 NUM_TO_U128_CHECKED: "toU128Checked"
145 NUM_TO_NAT: "toNat"
146 NUM_TO_NAT_CHECKED: "toNatChecked"
147 NUM_TO_F32: "toF32"
148 NUM_TO_F32_CHECKED: "toF32Checked"
149 NUM_TO_F64: "toF64"
150 NUM_TO_F64_CHECKED: "toF64Checked"
}
2 BOOL: "Bool" => {
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
@ -1103,7 +1107,6 @@ define_builtins! {
32 STR_TO_I16: "toI16"
33 STR_TO_U8: "toU8"
34 STR_TO_I8: "toI8"
}
4 LIST: "List" => {
0 LIST_LIST: "List" imported // the List.List type alias

View file

@ -1001,7 +1001,9 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
NumToStr | NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked
| NumRound | NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos
| NumAsin | NumIntCast | NumToIntChecked => arena.alloc_slice_copy(&[irrelevant]),
| NumAsin | NumIntCast | NumToIntChecked | NumToFloatCast | NumToFloatChecked => {
arena.alloc_slice_copy(&[irrelevant])
}
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),

View file

@ -170,6 +170,7 @@ enum FirstOrder {
NumBytesToU32,
NumShiftRightZfBy,
NumIntCast,
NumFloatCast,
Eq,
NotEq,
And,

View file

@ -5271,6 +5271,21 @@ mod solve_expr {
)
}
#[test]
fn to_float() {
infer_eq_without_problem(
indoc!(
r#"
{
toF32: Num.toF32,
toF64: Num.toF64,
}
"#
),
r#"{ toF32 : Num * -> F32, toF64 : Num * -> F64 }"#,
)
}
#[test]
fn opaque_wrap_infer() {
infer_eq_without_problem(