diff --git a/crates/compiler/builtins/bitcode/src/num.zig b/crates/compiler/builtins/bitcode/src/num.zig index f980b64901..e85a16d8c8 100644 --- a/crates/compiler/builtins/bitcode/src/num.zig +++ b/crates/compiler/builtins/bitcode/src/num.zig @@ -110,7 +110,21 @@ pub fn exportNumToFloatCast(comptime T: type, comptime F: type, comptime name: [ pub fn exportPow(comptime T: type, comptime name: []const u8) void { comptime var f = struct { fn func(base: T, exp: T) callconv(.C) T { - return std.math.pow(T, base, exp); + switch (@typeInfo(T)) { + // std.math.pow can handle ints via powi, but it turns any errors to unreachable + // we want to catch overflow and report a proper error to the user + .Int => { + if (std.math.powi(T, base, exp)) |value| { + return value; + } else |err| switch (err) { + error.Overflow => roc_panic("Integer raised to power overflowed!", 0), + error.Underflow => return 0, + } + }, + else => { + return std.math.pow(T, base, exp); + }, + } } }.func; @export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong }); diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index a17f97e93c..d9560ece5a 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -1829,24 +1829,12 @@ impl<'a> LowLevelCall<'a> { _ => panic_ret_type(), } } - NumPowInt => { - self.load_args(backend); - let base_type = CodeGenNumType::for_symbol(backend, self.arguments[0]); - let exponent_type = CodeGenNumType::for_symbol(backend, self.arguments[1]); - let ret_type = CodeGenNumType::from(self.ret_layout); - - debug_assert!(base_type == exponent_type); - debug_assert!(exponent_type == ret_type); - - let width = match ret_type { - CodeGenNumType::I32 => IntWidth::I32, - CodeGenNumType::I64 => IntWidth::I64, - CodeGenNumType::I128 => todo!("{:?} for I128", self.lowlevel), - _ => internal_error!("Invalid return type for pow: {:?}", ret_type), - }; - - self.load_args_and_call_zig(backend, &bitcode::NUM_POW_INT[width]) - } + NumPowInt => match self.ret_layout_raw { + LayoutRepr::Builtin(Builtin::Int(width)) => { + self.load_args_and_call_zig(backend, &bitcode::NUM_POW_INT[width]) + } + _ => panic_ret_type(), + }, NumIsNan => num_is_nan(backend, self.arguments[0]), NumIsInfinite => num_is_infinite(backend, self.arguments[0]), diff --git a/crates/compiler/test_gen/src/gen_num.rs b/crates/compiler/test_gen/src/gen_num.rs index cfc62532c8..d19026cac6 100644 --- a/crates/compiler/test_gen/src/gen_num.rs +++ b/crates/compiler/test_gen/src/gen_num.rs @@ -1926,6 +1926,13 @@ fn pow_int() { assert_evals_to!("Num.powInt 2 3", 8, i64); } +#[test] +#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] +#[should_panic(expected = r#"Roc failed with message: "Integer raised to power overflowed!"#)] +fn pow_int_overflow() { + assert_evals_to!("Num.powInt 2u8 8", 0, u8); +} + #[test] #[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))] fn atan() {