diff --git a/compiler/builtins/docs/Num.roc b/compiler/builtins/docs/Num.roc index df39d4849d..34fb2ca201 100644 --- a/compiler/builtins/docs/Num.roc +++ b/compiler/builtins/docs/Num.roc @@ -65,6 +65,7 @@ interface Num maxI128, maxInt, minFloat, + minI128, minInt, modInt, modFloat, @@ -822,6 +823,25 @@ maxU32 : U32 ## and zero is the lowest unsigned number. Unsigned numbers cannot be negative. minU32 : U32 +## The highest number that can be stored in an #I128 without overflowing its +## available memory and crashing. +## +## For reference, this number is `170_141_183_460_469_231_731_687_303_715_884_105_727`, +## which is over 2 million. +## +## Note that this is smaller than the positive version of #Int.minI128, +## which means if you call #Num.abs on #Int.minI128, it will overflow and crash! +maxI128 : I128 + +## The min number that can be stored in an #I128 without underflowing its +## available memory and crashing. +## +## For reference, this number is `-170_141_183_460_469_231_731_687_303_715_884_105_728`. +## +## Note that the positive version of this number is larger than #Int.maxI128, +## which means if you call #Num.abs on #Int.minI128, it will overflow and crash! +minI128 : I128 + ## The highest supported #F64 value you can have, which is approximately 1.8 × 10^308. ## ## If you go higher than this, your running Roc code will crash - so be careful not to! diff --git a/compiler/builtins/src/std.rs b/compiler/builtins/src/std.rs index db6d917a7c..d338d34959 100644 --- a/compiler/builtins/src/std.rs +++ b/compiler/builtins/src/std.rs @@ -397,6 +397,9 @@ pub fn types() -> MutMap { Box::new(bool_type()), ); + // minI128 : I128 + add_type!(Symbol::NUM_MIN_I128, i128_type()); + // maxI128 : I128 add_type!(Symbol::NUM_MAX_I128, i128_type()); diff --git a/compiler/can/src/builtins.rs b/compiler/can/src/builtins.rs index 04a1a1d9c8..696df17538 100644 --- a/compiler/can/src/builtins.rs +++ b/compiler/can/src/builtins.rs @@ -206,6 +206,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option NUM_SHIFT_RIGHT => num_shift_right_by, NUM_SHIFT_RIGHT_ZERO_FILL => num_shift_right_zf_by, NUM_INT_CAST=> num_int_cast, + NUM_MIN_I128=> num_min_i128, NUM_MAX_I128=> num_max_i128, NUM_TO_STR => num_to_str, RESULT_MAP => result_map, @@ -1237,6 +1238,35 @@ fn num_int_cast(symbol: Symbol, var_store: &mut VarStore) -> Def { lowlevel_1(symbol, LowLevel::NumIntCast, var_store) } +/// Num.minI128: I128 +fn num_min_i128(symbol: Symbol, var_store: &mut VarStore) -> Def { + let int_var = var_store.fresh(); + let int_precision_var = var_store.fresh(); + // TODO: or `i128::MIN.into()` ? + let body = int(int_var, int_precision_var, i128::MIN); + + let std = roc_builtins::std::types(); + let solved = std.get(&symbol).unwrap(); + let mut free_vars = roc_types::solved_types::FreeVars::default(); + let signature = roc_types::solved_types::to_type(&solved.0, &mut free_vars, var_store); + + let annotation = crate::def::Annotation { + signature, + introduced_variables: Default::default(), + region: Region::zero(), + aliases: Default::default(), + }; + + Def { + // TODO: or `None` ? + annotation: Some(annotation), + expr_var: int_var, + loc_expr: Loc::at_zero(body), + loc_pattern: Loc::at_zero(Pattern::Identifier(symbol)), + pattern_vars: SendMap::default(), + } +} + /// Num.maxI128: I128 fn num_max_i128(symbol: Symbol, var_store: &mut VarStore) -> Def { let int_var = var_store.fresh(); diff --git a/compiler/module/src/symbol.rs b/compiler/module/src/symbol.rs index 237f72b199..f2e4c31059 100644 --- a/compiler/module/src/symbol.rs +++ b/compiler/module/src/symbol.rs @@ -995,6 +995,7 @@ define_builtins! { 107 NUM_CAST_TO_NAT: "#castToNat" 108 NUM_DIV_CEIL: "divCeil" 109 NUM_TO_STR: "toStr" + 110 NUM_MIN_I128: "minI128" } 2 BOOL: "Bool" => { 0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias diff --git a/compiler/solve/tests/solve_expr.rs b/compiler/solve/tests/solve_expr.rs index 7ada9982f1..2a4e285971 100644 --- a/compiler/solve/tests/solve_expr.rs +++ b/compiler/solve/tests/solve_expr.rs @@ -3341,6 +3341,18 @@ mod solve_expr { ); } + #[test] + fn min_i128() { + infer_eq_without_problem( + indoc!( + r#" + Num.minI128 + "# + ), + "I128", + ); + } + #[test] fn max_i128() { infer_eq_without_problem( diff --git a/compiler/test_gen/src/gen_num.rs b/compiler/test_gen/src/gen_num.rs index 3fc1227f70..88096f5b39 100644 --- a/compiler/test_gen/src/gen_num.rs +++ b/compiler/test_gen/src/gen_num.rs @@ -1829,6 +1829,20 @@ fn shift_right_zf_by() { assert_evals_to!("Num.shiftRightBy 3 0b0000_1100u8", 0b0000_0011, i64); } +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] +fn min_i128() { + assert_evals_to!( + indoc!( + r#" + Num.minI128 + "# + ), + i128::MIN, + i128 + ); +} + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))] fn max_i128() {