diff --git a/compiler/gen_llvm/src/llvm/build.rs b/compiler/gen_llvm/src/llvm/build.rs index b2ffe99c53..8b1530dbd0 100644 --- a/compiler/gen_llvm/src/llvm/build.rs +++ b/compiler/gen_llvm/src/llvm/build.rs @@ -443,13 +443,7 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) { ctx.struct_type(&fields, false) .fn_type(&[i64_type.into(), i64_type.into()], false) }); - - // TODO: This is now exported by zig. Not sure what to do here? - // add_intrinsic(module, LLVM_SADD_WITH_OVERFLOW_I128, { - // let fields = [i128_type.into(), i1_type.into()]; - // ctx.struct_type(&fields, false) - // .fn_type(&[i128_type.into(), i128_type.into()], false) - // }); + // LLVM_SADD_WITH_OVERFLOW_I128 is expoerted in bitcode // sub with overflow @@ -476,13 +470,7 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) { ctx.struct_type(&fields, false) .fn_type(&[i64_type.into(), i64_type.into()], false) }); - - // TODO: This is now exported by zig. Not sure what to do here? - // add_intrinsic(module, LLVM_SSUB_WITH_OVERFLOW_I128, { - // let fields = [i128_type.into(), i1_type.into()]; - // ctx.struct_type(&fields, false) - // .fn_type(&[i128_type.into(), i128_type.into()], false) - // }); + // LLVM_SSUB_WITH_OVERFLOW_I128 is expoerted in bitcode } static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64"; diff --git a/compiler/gen_llvm/src/llvm/build_hash.rs b/compiler/gen_llvm/src/llvm/build_hash.rs index 5f0e3c3bd5..a35b7b5e72 100644 --- a/compiler/gen_llvm/src/llvm/build_hash.rs +++ b/compiler/gen_llvm/src/llvm/build_hash.rs @@ -127,13 +127,11 @@ fn hash_builtin<'a, 'ctx, 'env>( | Builtin::Float32 | Builtin::Float128 | Builtin::Float16 + | Builtin::Decimal | Builtin::Usize => { let hash_bytes = store_and_use_as_u8_ptr(env, val, &layout); hash_bitcode_fn(env, seed, hash_bytes, layout.stack_size(ptr_bytes)) } - Builtin::Decimal => { - panic!("TODO: Hash Decimal"); - } Builtin::Str => { // let zig deal with big vs small string call_bitcode_fn( diff --git a/compiler/test_gen/src/gen_num.rs b/compiler/test_gen/src/gen_num.rs index f08065ae4c..4d73a27546 100644 --- a/compiler/test_gen/src/gen_num.rs +++ b/compiler/test_gen/src/gen_num.rs @@ -3,7 +3,7 @@ mod gen_num { use crate::assert_evals_to; use crate::assert_llvm_evals_to; use indoc::indoc; - use roc_std::RocOrder; + use roc_std::{RocDec, RocOrder}; #[test] fn nat_alias() { @@ -339,7 +339,7 @@ mod gen_num { x "# ), - 2100000000000000000, + RocDec::from_str_to_i128_unsafe(&"2.1"), i128 ); } @@ -576,7 +576,7 @@ mod gen_num { z "# ), - 5200000000000000000, + RocDec::from_str_to_i128_unsafe(&"5.2"), i128 ); } @@ -639,7 +639,7 @@ mod gen_num { Err _ -> -1 "# ), - 3333333333333333333, + RocDec::from_str_to_i128_unsafe(&"3.333333333333333333"), i128 ); } @@ -755,7 +755,7 @@ mod gen_num { x - y - z "# ), - -3900000000000000000, + RocDec::from_str_to_i128_unsafe(&"-3.9"), i128 ); } @@ -803,7 +803,7 @@ mod gen_num { x * y * z "# ), - 48000000000000000000, + RocDec::from_str_to_i128_unsafe(&"48.0"), i128 ); } diff --git a/roc_std/src/lib.rs b/roc_std/src/lib.rs index b54fd6c7b9..c7206132d7 100644 --- a/roc_std/src/lib.rs +++ b/roc_std/src/lib.rs @@ -1,5 +1,6 @@ #![crate_type = "lib"] #![no_std] +use core::convert::From; use core::ffi::c_void; use core::{fmt, mem, ptr}; @@ -694,3 +695,74 @@ impl<'a, T: Sized + Copy> From<&'a RocCallResult> for Result { } } } + +#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] +pub struct RocDec(pub i128); + +impl RocDec { + pub const MIN: Self = Self(i128::MIN); + pub const MAX: Self = Self(i128::MAX); + + pub const DECIMAL_PLACES: u32 = 18; + + pub const ONE_POINT_ZERO: i128 = 10i128.pow(Self::DECIMAL_PLACES); + + pub fn from_str(value: &str) -> Result { + // Split the string into the parts before and after the "." + let mut parts = value.split("."); + + let before_point = match parts.next() { + Some(answer) => answer, + None => { + return Err(()); + } + }; + + let after_point = match parts.next() { + Some(answer) if answer.len() <= Self::DECIMAL_PLACES as usize => answer, + _ => { + return Err(()); + } + }; + + // There should have only been one "." in the string! + if parts.next().is_some() { + return Err(()); + } + + // Calculate the low digits - the ones after the decimal point. + let lo = match after_point.parse::() { + Ok(answer) => { + // Translate e.g. the 1 from 0.1 into 10000000000000000000 + // by "restoring" the elided trailing zeroes to the number! + let trailing_zeroes = Self::DECIMAL_PLACES as usize - after_point.len(); + let lo = answer * 10i128.pow(trailing_zeroes as u32); + + if !before_point.starts_with("-") { + lo + } else { + -lo + } + } + Err(_) => { + return Err(()); + } + }; + + // Calculate the high digits - the ones before the decimal point. + match before_point.parse::() { + Ok(answer) => match answer.checked_mul(10i128.pow(Self::DECIMAL_PLACES)) { + Some(hi) => match hi.checked_add(lo) { + Some(num) => Ok(RocDec(num)), + None => Err(()), + }, + None => Err(()), + }, + Err(_) => Err(()), + } + } + + pub fn from_str_to_i128_unsafe(val: &str) -> i128 { + RocDec::from_str(val).unwrap().0 + } +}