diff --git a/src/build/builtin_compiler/main.zig b/src/build/builtin_compiler/main.zig index 013e300887..f28e362bc5 100644 --- a/src/build/builtin_compiler/main.zig +++ b/src/build/builtin_compiler/main.zig @@ -454,7 +454,679 @@ fn replaceStrIsEmptyWithLowLevel(env: *ModuleEnv) !std.ArrayList(CIR.Def.Idx) { try low_level_map.put(ident, .i8_to_dec); } + // U16 conversion operations + if (env.common.findIdent("Builtin.Num.U16.to_i8_wrap")) |ident| { + try low_level_map.put(ident, .u16_to_i8_wrap); + } + if (env.common.findIdent("Builtin.Num.U16.to_i8_try")) |ident| { + try low_level_map.put(ident, .u16_to_i8_try); + } + if (env.common.findIdent("Builtin.Num.U16.to_i16_wrap")) |ident| { + try low_level_map.put(ident, .u16_to_i16_wrap); + } + if (env.common.findIdent("Builtin.Num.U16.to_i16_try")) |ident| { + try low_level_map.put(ident, .u16_to_i16_try); + } + if (env.common.findIdent("Builtin.Num.U16.to_i32")) |ident| { + try low_level_map.put(ident, .u16_to_i32); + } + if (env.common.findIdent("Builtin.Num.U16.to_i64")) |ident| { + try low_level_map.put(ident, .u16_to_i64); + } + if (env.common.findIdent("Builtin.Num.U16.to_i128")) |ident| { + try low_level_map.put(ident, .u16_to_i128); + } + if (env.common.findIdent("Builtin.Num.U16.to_u8_wrap")) |ident| { + try low_level_map.put(ident, .u16_to_u8_wrap); + } + if (env.common.findIdent("Builtin.Num.U16.to_u8_try")) |ident| { + try low_level_map.put(ident, .u16_to_u8_try); + } + if (env.common.findIdent("Builtin.Num.U16.to_u32")) |ident| { + try low_level_map.put(ident, .u16_to_u32); + } + if (env.common.findIdent("Builtin.Num.U16.to_u64")) |ident| { + try low_level_map.put(ident, .u16_to_u64); + } + if (env.common.findIdent("Builtin.Num.U16.to_u128")) |ident| { + try low_level_map.put(ident, .u16_to_u128); + } + if (env.common.findIdent("Builtin.Num.U16.to_f32")) |ident| { + try low_level_map.put(ident, .u16_to_f32); + } + if (env.common.findIdent("Builtin.Num.U16.to_f64")) |ident| { + try low_level_map.put(ident, .u16_to_f64); + } + if (env.common.findIdent("Builtin.Num.U16.to_dec")) |ident| { + try low_level_map.put(ident, .u16_to_dec); + } + + // I16 conversion operations + if (env.common.findIdent("Builtin.Num.I16.to_i8_wrap")) |ident| { + try low_level_map.put(ident, .i16_to_i8_wrap); + } + if (env.common.findIdent("Builtin.Num.I16.to_i8_try")) |ident| { + try low_level_map.put(ident, .i16_to_i8_try); + } + if (env.common.findIdent("Builtin.Num.I16.to_i32")) |ident| { + try low_level_map.put(ident, .i16_to_i32); + } + if (env.common.findIdent("Builtin.Num.I16.to_i64")) |ident| { + try low_level_map.put(ident, .i16_to_i64); + } + if (env.common.findIdent("Builtin.Num.I16.to_i128")) |ident| { + try low_level_map.put(ident, .i16_to_i128); + } + if (env.common.findIdent("Builtin.Num.I16.to_u8_wrap")) |ident| { + try low_level_map.put(ident, .i16_to_u8_wrap); + } + if (env.common.findIdent("Builtin.Num.I16.to_u8_try")) |ident| { + try low_level_map.put(ident, .i16_to_u8_try); + } + if (env.common.findIdent("Builtin.Num.I16.to_u16_wrap")) |ident| { + try low_level_map.put(ident, .i16_to_u16_wrap); + } + if (env.common.findIdent("Builtin.Num.I16.to_u16_try")) |ident| { + try low_level_map.put(ident, .i16_to_u16_try); + } + if (env.common.findIdent("Builtin.Num.I16.to_u32_wrap")) |ident| { + try low_level_map.put(ident, .i16_to_u32_wrap); + } + if (env.common.findIdent("Builtin.Num.I16.to_u32_try")) |ident| { + try low_level_map.put(ident, .i16_to_u32_try); + } + if (env.common.findIdent("Builtin.Num.I16.to_u64_wrap")) |ident| { + try low_level_map.put(ident, .i16_to_u64_wrap); + } + if (env.common.findIdent("Builtin.Num.I16.to_u64_try")) |ident| { + try low_level_map.put(ident, .i16_to_u64_try); + } + if (env.common.findIdent("Builtin.Num.I16.to_u128_wrap")) |ident| { + try low_level_map.put(ident, .i16_to_u128_wrap); + } + if (env.common.findIdent("Builtin.Num.I16.to_u128_try")) |ident| { + try low_level_map.put(ident, .i16_to_u128_try); + } + if (env.common.findIdent("Builtin.Num.I16.to_f32")) |ident| { + try low_level_map.put(ident, .i16_to_f32); + } + if (env.common.findIdent("Builtin.Num.I16.to_f64")) |ident| { + try low_level_map.put(ident, .i16_to_f64); + } + if (env.common.findIdent("Builtin.Num.I16.to_dec")) |ident| { + try low_level_map.put(ident, .i16_to_dec); + } + + // U32 conversion operations + if (env.common.findIdent("Builtin.Num.U32.to_i8_wrap")) |ident| { + try low_level_map.put(ident, .u32_to_i8_wrap); + } + if (env.common.findIdent("Builtin.Num.U32.to_i8_try")) |ident| { + try low_level_map.put(ident, .u32_to_i8_try); + } + if (env.common.findIdent("Builtin.Num.U32.to_i16_wrap")) |ident| { + try low_level_map.put(ident, .u32_to_i16_wrap); + } + if (env.common.findIdent("Builtin.Num.U32.to_i16_try")) |ident| { + try low_level_map.put(ident, .u32_to_i16_try); + } + if (env.common.findIdent("Builtin.Num.U32.to_i32_wrap")) |ident| { + try low_level_map.put(ident, .u32_to_i32_wrap); + } + if (env.common.findIdent("Builtin.Num.U32.to_i32_try")) |ident| { + try low_level_map.put(ident, .u32_to_i32_try); + } + if (env.common.findIdent("Builtin.Num.U32.to_i64")) |ident| { + try low_level_map.put(ident, .u32_to_i64); + } + if (env.common.findIdent("Builtin.Num.U32.to_i128")) |ident| { + try low_level_map.put(ident, .u32_to_i128); + } + if (env.common.findIdent("Builtin.Num.U32.to_u8_wrap")) |ident| { + try low_level_map.put(ident, .u32_to_u8_wrap); + } + if (env.common.findIdent("Builtin.Num.U32.to_u8_try")) |ident| { + try low_level_map.put(ident, .u32_to_u8_try); + } + if (env.common.findIdent("Builtin.Num.U32.to_u16_wrap")) |ident| { + try low_level_map.put(ident, .u32_to_u16_wrap); + } + if (env.common.findIdent("Builtin.Num.U32.to_u16_try")) |ident| { + try low_level_map.put(ident, .u32_to_u16_try); + } + if (env.common.findIdent("Builtin.Num.U32.to_u64")) |ident| { + try low_level_map.put(ident, .u32_to_u64); + } + if (env.common.findIdent("Builtin.Num.U32.to_u128")) |ident| { + try low_level_map.put(ident, .u32_to_u128); + } + if (env.common.findIdent("Builtin.Num.U32.to_f32")) |ident| { + try low_level_map.put(ident, .u32_to_f32); + } + if (env.common.findIdent("Builtin.Num.U32.to_f64")) |ident| { + try low_level_map.put(ident, .u32_to_f64); + } + if (env.common.findIdent("Builtin.Num.U32.to_dec")) |ident| { + try low_level_map.put(ident, .u32_to_dec); + } + + // I32 conversion operations + if (env.common.findIdent("Builtin.Num.I32.to_i8_wrap")) |ident| { + try low_level_map.put(ident, .i32_to_i8_wrap); + } + if (env.common.findIdent("Builtin.Num.I32.to_i8_try")) |ident| { + try low_level_map.put(ident, .i32_to_i8_try); + } + if (env.common.findIdent("Builtin.Num.I32.to_i16_wrap")) |ident| { + try low_level_map.put(ident, .i32_to_i16_wrap); + } + if (env.common.findIdent("Builtin.Num.I32.to_i16_try")) |ident| { + try low_level_map.put(ident, .i32_to_i16_try); + } + if (env.common.findIdent("Builtin.Num.I32.to_i64")) |ident| { + try low_level_map.put(ident, .i32_to_i64); + } + if (env.common.findIdent("Builtin.Num.I32.to_i128")) |ident| { + try low_level_map.put(ident, .i32_to_i128); + } + if (env.common.findIdent("Builtin.Num.I32.to_u8_wrap")) |ident| { + try low_level_map.put(ident, .i32_to_u8_wrap); + } + if (env.common.findIdent("Builtin.Num.I32.to_u8_try")) |ident| { + try low_level_map.put(ident, .i32_to_u8_try); + } + if (env.common.findIdent("Builtin.Num.I32.to_u16_wrap")) |ident| { + try low_level_map.put(ident, .i32_to_u16_wrap); + } + if (env.common.findIdent("Builtin.Num.I32.to_u16_try")) |ident| { + try low_level_map.put(ident, .i32_to_u16_try); + } + if (env.common.findIdent("Builtin.Num.I32.to_u32_wrap")) |ident| { + try low_level_map.put(ident, .i32_to_u32_wrap); + } + if (env.common.findIdent("Builtin.Num.I32.to_u32_try")) |ident| { + try low_level_map.put(ident, .i32_to_u32_try); + } + if (env.common.findIdent("Builtin.Num.I32.to_u64_wrap")) |ident| { + try low_level_map.put(ident, .i32_to_u64_wrap); + } + if (env.common.findIdent("Builtin.Num.I32.to_u64_try")) |ident| { + try low_level_map.put(ident, .i32_to_u64_try); + } + if (env.common.findIdent("Builtin.Num.I32.to_u128_wrap")) |ident| { + try low_level_map.put(ident, .i32_to_u128_wrap); + } + if (env.common.findIdent("Builtin.Num.I32.to_u128_try")) |ident| { + try low_level_map.put(ident, .i32_to_u128_try); + } + if (env.common.findIdent("Builtin.Num.I32.to_f32")) |ident| { + try low_level_map.put(ident, .i32_to_f32); + } + if (env.common.findIdent("Builtin.Num.I32.to_f64")) |ident| { + try low_level_map.put(ident, .i32_to_f64); + } + if (env.common.findIdent("Builtin.Num.I32.to_dec")) |ident| { + try low_level_map.put(ident, .i32_to_dec); + } + + // U64 conversion operations + if (env.common.findIdent("Builtin.Num.U64.to_i8_wrap")) |ident| { + try low_level_map.put(ident, .u64_to_i8_wrap); + } + if (env.common.findIdent("Builtin.Num.U64.to_i8_try")) |ident| { + try low_level_map.put(ident, .u64_to_i8_try); + } + if (env.common.findIdent("Builtin.Num.U64.to_i16_wrap")) |ident| { + try low_level_map.put(ident, .u64_to_i16_wrap); + } + if (env.common.findIdent("Builtin.Num.U64.to_i16_try")) |ident| { + try low_level_map.put(ident, .u64_to_i16_try); + } + if (env.common.findIdent("Builtin.Num.U64.to_i32_wrap")) |ident| { + try low_level_map.put(ident, .u64_to_i32_wrap); + } + if (env.common.findIdent("Builtin.Num.U64.to_i32_try")) |ident| { + try low_level_map.put(ident, .u64_to_i32_try); + } + if (env.common.findIdent("Builtin.Num.U64.to_i64_wrap")) |ident| { + try low_level_map.put(ident, .u64_to_i64_wrap); + } + if (env.common.findIdent("Builtin.Num.U64.to_i64_try")) |ident| { + try low_level_map.put(ident, .u64_to_i64_try); + } + if (env.common.findIdent("Builtin.Num.U64.to_i128")) |ident| { + try low_level_map.put(ident, .u64_to_i128); + } + if (env.common.findIdent("Builtin.Num.U64.to_u8_wrap")) |ident| { + try low_level_map.put(ident, .u64_to_u8_wrap); + } + if (env.common.findIdent("Builtin.Num.U64.to_u8_try")) |ident| { + try low_level_map.put(ident, .u64_to_u8_try); + } + if (env.common.findIdent("Builtin.Num.U64.to_u16_wrap")) |ident| { + try low_level_map.put(ident, .u64_to_u16_wrap); + } + if (env.common.findIdent("Builtin.Num.U64.to_u16_try")) |ident| { + try low_level_map.put(ident, .u64_to_u16_try); + } + if (env.common.findIdent("Builtin.Num.U64.to_u32_wrap")) |ident| { + try low_level_map.put(ident, .u64_to_u32_wrap); + } + if (env.common.findIdent("Builtin.Num.U64.to_u32_try")) |ident| { + try low_level_map.put(ident, .u64_to_u32_try); + } + if (env.common.findIdent("Builtin.Num.U64.to_u128")) |ident| { + try low_level_map.put(ident, .u64_to_u128); + } + if (env.common.findIdent("Builtin.Num.U64.to_f32")) |ident| { + try low_level_map.put(ident, .u64_to_f32); + } + if (env.common.findIdent("Builtin.Num.U64.to_f64")) |ident| { + try low_level_map.put(ident, .u64_to_f64); + } + if (env.common.findIdent("Builtin.Num.U64.to_dec")) |ident| { + try low_level_map.put(ident, .u64_to_dec); + } + + // I64 conversion operations + if (env.common.findIdent("Builtin.Num.I64.to_i8_wrap")) |ident| { + try low_level_map.put(ident, .i64_to_i8_wrap); + } + if (env.common.findIdent("Builtin.Num.I64.to_i8_try")) |ident| { + try low_level_map.put(ident, .i64_to_i8_try); + } + if (env.common.findIdent("Builtin.Num.I64.to_i16_wrap")) |ident| { + try low_level_map.put(ident, .i64_to_i16_wrap); + } + if (env.common.findIdent("Builtin.Num.I64.to_i16_try")) |ident| { + try low_level_map.put(ident, .i64_to_i16_try); + } + if (env.common.findIdent("Builtin.Num.I64.to_i32_wrap")) |ident| { + try low_level_map.put(ident, .i64_to_i32_wrap); + } + if (env.common.findIdent("Builtin.Num.I64.to_i32_try")) |ident| { + try low_level_map.put(ident, .i64_to_i32_try); + } + if (env.common.findIdent("Builtin.Num.I64.to_i128")) |ident| { + try low_level_map.put(ident, .i64_to_i128); + } + if (env.common.findIdent("Builtin.Num.I64.to_u8_wrap")) |ident| { + try low_level_map.put(ident, .i64_to_u8_wrap); + } + if (env.common.findIdent("Builtin.Num.I64.to_u8_try")) |ident| { + try low_level_map.put(ident, .i64_to_u8_try); + } + if (env.common.findIdent("Builtin.Num.I64.to_u16_wrap")) |ident| { + try low_level_map.put(ident, .i64_to_u16_wrap); + } + if (env.common.findIdent("Builtin.Num.I64.to_u16_try")) |ident| { + try low_level_map.put(ident, .i64_to_u16_try); + } + if (env.common.findIdent("Builtin.Num.I64.to_u32_wrap")) |ident| { + try low_level_map.put(ident, .i64_to_u32_wrap); + } + if (env.common.findIdent("Builtin.Num.I64.to_u32_try")) |ident| { + try low_level_map.put(ident, .i64_to_u32_try); + } + if (env.common.findIdent("Builtin.Num.I64.to_u64_wrap")) |ident| { + try low_level_map.put(ident, .i64_to_u64_wrap); + } + if (env.common.findIdent("Builtin.Num.I64.to_u64_try")) |ident| { + try low_level_map.put(ident, .i64_to_u64_try); + } + if (env.common.findIdent("Builtin.Num.I64.to_u128_wrap")) |ident| { + try low_level_map.put(ident, .i64_to_u128_wrap); + } + if (env.common.findIdent("Builtin.Num.I64.to_u128_try")) |ident| { + try low_level_map.put(ident, .i64_to_u128_try); + } + if (env.common.findIdent("Builtin.Num.I64.to_f32")) |ident| { + try low_level_map.put(ident, .i64_to_f32); + } + if (env.common.findIdent("Builtin.Num.I64.to_f64")) |ident| { + try low_level_map.put(ident, .i64_to_f64); + } + if (env.common.findIdent("Builtin.Num.I64.to_dec")) |ident| { + try low_level_map.put(ident, .i64_to_dec); + } + + // U128 conversion operations + if (env.common.findIdent("Builtin.Num.U128.to_i8_wrap")) |ident| { + try low_level_map.put(ident, .u128_to_i8_wrap); + } + if (env.common.findIdent("Builtin.Num.U128.to_i8_try")) |ident| { + try low_level_map.put(ident, .u128_to_i8_try); + } + if (env.common.findIdent("Builtin.Num.U128.to_i16_wrap")) |ident| { + try low_level_map.put(ident, .u128_to_i16_wrap); + } + if (env.common.findIdent("Builtin.Num.U128.to_i16_try")) |ident| { + try low_level_map.put(ident, .u128_to_i16_try); + } + if (env.common.findIdent("Builtin.Num.U128.to_i32_wrap")) |ident| { + try low_level_map.put(ident, .u128_to_i32_wrap); + } + if (env.common.findIdent("Builtin.Num.U128.to_i32_try")) |ident| { + try low_level_map.put(ident, .u128_to_i32_try); + } + if (env.common.findIdent("Builtin.Num.U128.to_i64_wrap")) |ident| { + try low_level_map.put(ident, .u128_to_i64_wrap); + } + if (env.common.findIdent("Builtin.Num.U128.to_i64_try")) |ident| { + try low_level_map.put(ident, .u128_to_i64_try); + } + if (env.common.findIdent("Builtin.Num.U128.to_i128_wrap")) |ident| { + try low_level_map.put(ident, .u128_to_i128_wrap); + } + if (env.common.findIdent("Builtin.Num.U128.to_i128_try")) |ident| { + try low_level_map.put(ident, .u128_to_i128_try); + } + if (env.common.findIdent("Builtin.Num.U128.to_u8_wrap")) |ident| { + try low_level_map.put(ident, .u128_to_u8_wrap); + } + if (env.common.findIdent("Builtin.Num.U128.to_u8_try")) |ident| { + try low_level_map.put(ident, .u128_to_u8_try); + } + if (env.common.findIdent("Builtin.Num.U128.to_u16_wrap")) |ident| { + try low_level_map.put(ident, .u128_to_u16_wrap); + } + if (env.common.findIdent("Builtin.Num.U128.to_u16_try")) |ident| { + try low_level_map.put(ident, .u128_to_u16_try); + } + if (env.common.findIdent("Builtin.Num.U128.to_u32_wrap")) |ident| { + try low_level_map.put(ident, .u128_to_u32_wrap); + } + if (env.common.findIdent("Builtin.Num.U128.to_u32_try")) |ident| { + try low_level_map.put(ident, .u128_to_u32_try); + } + if (env.common.findIdent("Builtin.Num.U128.to_u64_wrap")) |ident| { + try low_level_map.put(ident, .u128_to_u64_wrap); + } + if (env.common.findIdent("Builtin.Num.U128.to_u64_try")) |ident| { + try low_level_map.put(ident, .u128_to_u64_try); + } + if (env.common.findIdent("Builtin.Num.U128.to_f32")) |ident| { + try low_level_map.put(ident, .u128_to_f32); + } + if (env.common.findIdent("Builtin.Num.U128.to_f64")) |ident| { + try low_level_map.put(ident, .u128_to_f64); + } + if (env.common.findIdent("u128_to_dec_try_unsafe")) |ident| { + try low_level_map.put(ident, .u128_to_dec_try_unsafe); + } + + // I128 conversion operations + if (env.common.findIdent("Builtin.Num.I128.to_i8_wrap")) |ident| { + try low_level_map.put(ident, .i128_to_i8_wrap); + } + if (env.common.findIdent("Builtin.Num.I128.to_i8_try")) |ident| { + try low_level_map.put(ident, .i128_to_i8_try); + } + if (env.common.findIdent("Builtin.Num.I128.to_i16_wrap")) |ident| { + try low_level_map.put(ident, .i128_to_i16_wrap); + } + if (env.common.findIdent("Builtin.Num.I128.to_i16_try")) |ident| { + try low_level_map.put(ident, .i128_to_i16_try); + } + if (env.common.findIdent("Builtin.Num.I128.to_i32_wrap")) |ident| { + try low_level_map.put(ident, .i128_to_i32_wrap); + } + if (env.common.findIdent("Builtin.Num.I128.to_i32_try")) |ident| { + try low_level_map.put(ident, .i128_to_i32_try); + } + if (env.common.findIdent("Builtin.Num.I128.to_i64_wrap")) |ident| { + try low_level_map.put(ident, .i128_to_i64_wrap); + } + if (env.common.findIdent("Builtin.Num.I128.to_i64_try")) |ident| { + try low_level_map.put(ident, .i128_to_i64_try); + } + if (env.common.findIdent("Builtin.Num.I128.to_u8_wrap")) |ident| { + try low_level_map.put(ident, .i128_to_u8_wrap); + } + if (env.common.findIdent("Builtin.Num.I128.to_u8_try")) |ident| { + try low_level_map.put(ident, .i128_to_u8_try); + } + if (env.common.findIdent("Builtin.Num.I128.to_u16_wrap")) |ident| { + try low_level_map.put(ident, .i128_to_u16_wrap); + } + if (env.common.findIdent("Builtin.Num.I128.to_u16_try")) |ident| { + try low_level_map.put(ident, .i128_to_u16_try); + } + if (env.common.findIdent("Builtin.Num.I128.to_u32_wrap")) |ident| { + try low_level_map.put(ident, .i128_to_u32_wrap); + } + if (env.common.findIdent("Builtin.Num.I128.to_u32_try")) |ident| { + try low_level_map.put(ident, .i128_to_u32_try); + } + if (env.common.findIdent("Builtin.Num.I128.to_u64_wrap")) |ident| { + try low_level_map.put(ident, .i128_to_u64_wrap); + } + if (env.common.findIdent("Builtin.Num.I128.to_u64_try")) |ident| { + try low_level_map.put(ident, .i128_to_u64_try); + } + if (env.common.findIdent("Builtin.Num.I128.to_u128_wrap")) |ident| { + try low_level_map.put(ident, .i128_to_u128_wrap); + } + if (env.common.findIdent("Builtin.Num.I128.to_u128_try")) |ident| { + try low_level_map.put(ident, .i128_to_u128_try); + } + if (env.common.findIdent("Builtin.Num.I128.to_f32")) |ident| { + try low_level_map.put(ident, .i128_to_f32); + } + if (env.common.findIdent("Builtin.Num.I128.to_f64")) |ident| { + try low_level_map.put(ident, .i128_to_f64); + } + if (env.common.findIdent("i128_to_dec_try_unsafe")) |ident| { + try low_level_map.put(ident, .i128_to_dec_try_unsafe); + } + + // F32 conversion operations + if (env.common.findIdent("Builtin.Num.F32.to_i8_trunc")) |ident| { + try low_level_map.put(ident, .f32_to_i8_trunc); + } + if (env.common.findIdent("f32_to_i8_try_unsafe")) |ident| { + try low_level_map.put(ident, .f32_to_i8_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F32.to_i16_trunc")) |ident| { + try low_level_map.put(ident, .f32_to_i16_trunc); + } + if (env.common.findIdent("f32_to_i16_try_unsafe")) |ident| { + try low_level_map.put(ident, .f32_to_i16_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F32.to_i32_trunc")) |ident| { + try low_level_map.put(ident, .f32_to_i32_trunc); + } + if (env.common.findIdent("f32_to_i32_try_unsafe")) |ident| { + try low_level_map.put(ident, .f32_to_i32_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F32.to_i64_trunc")) |ident| { + try low_level_map.put(ident, .f32_to_i64_trunc); + } + if (env.common.findIdent("f32_to_i64_try_unsafe")) |ident| { + try low_level_map.put(ident, .f32_to_i64_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F32.to_i128_trunc")) |ident| { + try low_level_map.put(ident, .f32_to_i128_trunc); + } + if (env.common.findIdent("f32_to_i128_try_unsafe")) |ident| { + try low_level_map.put(ident, .f32_to_i128_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F32.to_u8_trunc")) |ident| { + try low_level_map.put(ident, .f32_to_u8_trunc); + } + if (env.common.findIdent("f32_to_u8_try_unsafe")) |ident| { + try low_level_map.put(ident, .f32_to_u8_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F32.to_u16_trunc")) |ident| { + try low_level_map.put(ident, .f32_to_u16_trunc); + } + if (env.common.findIdent("f32_to_u16_try_unsafe")) |ident| { + try low_level_map.put(ident, .f32_to_u16_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F32.to_u32_trunc")) |ident| { + try low_level_map.put(ident, .f32_to_u32_trunc); + } + if (env.common.findIdent("f32_to_u32_try_unsafe")) |ident| { + try low_level_map.put(ident, .f32_to_u32_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F32.to_u64_trunc")) |ident| { + try low_level_map.put(ident, .f32_to_u64_trunc); + } + if (env.common.findIdent("f32_to_u64_try_unsafe")) |ident| { + try low_level_map.put(ident, .f32_to_u64_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F32.to_u128_trunc")) |ident| { + try low_level_map.put(ident, .f32_to_u128_trunc); + } + if (env.common.findIdent("f32_to_u128_try_unsafe")) |ident| { + try low_level_map.put(ident, .f32_to_u128_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F32.to_f64")) |ident| { + try low_level_map.put(ident, .f32_to_f64); + } + + // F64 conversion operations + if (env.common.findIdent("Builtin.Num.F64.to_i8_trunc")) |ident| { + try low_level_map.put(ident, .f64_to_i8_trunc); + } + if (env.common.findIdent("f64_to_i8_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_i8_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F64.to_i16_trunc")) |ident| { + try low_level_map.put(ident, .f64_to_i16_trunc); + } + if (env.common.findIdent("f64_to_i16_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_i16_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F64.to_i32_trunc")) |ident| { + try low_level_map.put(ident, .f64_to_i32_trunc); + } + if (env.common.findIdent("f64_to_i32_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_i32_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F64.to_i64_trunc")) |ident| { + try low_level_map.put(ident, .f64_to_i64_trunc); + } + if (env.common.findIdent("f64_to_i64_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_i64_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F64.to_i128_trunc")) |ident| { + try low_level_map.put(ident, .f64_to_i128_trunc); + } + if (env.common.findIdent("f64_to_i128_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_i128_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F64.to_u8_trunc")) |ident| { + try low_level_map.put(ident, .f64_to_u8_trunc); + } + if (env.common.findIdent("f64_to_u8_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_u8_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F64.to_u16_trunc")) |ident| { + try low_level_map.put(ident, .f64_to_u16_trunc); + } + if (env.common.findIdent("f64_to_u16_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_u16_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F64.to_u32_trunc")) |ident| { + try low_level_map.put(ident, .f64_to_u32_trunc); + } + if (env.common.findIdent("f64_to_u32_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_u32_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F64.to_u64_trunc")) |ident| { + try low_level_map.put(ident, .f64_to_u64_trunc); + } + if (env.common.findIdent("f64_to_u64_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_u64_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F64.to_u128_trunc")) |ident| { + try low_level_map.put(ident, .f64_to_u128_trunc); + } + if (env.common.findIdent("f64_to_u128_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_u128_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.F64.to_f32_wrap")) |ident| { + try low_level_map.put(ident, .f64_to_f32_wrap); + } + if (env.common.findIdent("f64_to_f32_try_unsafe")) |ident| { + try low_level_map.put(ident, .f64_to_f32_try_unsafe); + } + + // Dec conversion functions + if (env.common.findIdent("Builtin.Num.Dec.to_i8_trunc")) |ident| { + try low_level_map.put(ident, .dec_to_i8_trunc); + } + if (env.common.findIdent("dec_to_i8_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_i8_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_i16_trunc")) |ident| { + try low_level_map.put(ident, .dec_to_i16_trunc); + } + if (env.common.findIdent("dec_to_i16_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_i16_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_i32_trunc")) |ident| { + try low_level_map.put(ident, .dec_to_i32_trunc); + } + if (env.common.findIdent("dec_to_i32_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_i32_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_i64_trunc")) |ident| { + try low_level_map.put(ident, .dec_to_i64_trunc); + } + if (env.common.findIdent("dec_to_i64_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_i64_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_i128_trunc")) |ident| { + try low_level_map.put(ident, .dec_to_i128_trunc); + } + if (env.common.findIdent("dec_to_i128_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_i128_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_u8_trunc")) |ident| { + try low_level_map.put(ident, .dec_to_u8_trunc); + } + if (env.common.findIdent("dec_to_u8_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_u8_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_u16_trunc")) |ident| { + try low_level_map.put(ident, .dec_to_u16_trunc); + } + if (env.common.findIdent("dec_to_u16_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_u16_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_u32_trunc")) |ident| { + try low_level_map.put(ident, .dec_to_u32_trunc); + } + if (env.common.findIdent("dec_to_u32_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_u32_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_u64_trunc")) |ident| { + try low_level_map.put(ident, .dec_to_u64_trunc); + } + if (env.common.findIdent("dec_to_u64_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_u64_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_u128_trunc")) |ident| { + try low_level_map.put(ident, .dec_to_u128_trunc); + } + if (env.common.findIdent("dec_to_u128_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_u128_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_f32_wrap")) |ident| { + try low_level_map.put(ident, .dec_to_f32_wrap); + } + if (env.common.findIdent("dec_to_f32_try_unsafe")) |ident| { + try low_level_map.put(ident, .dec_to_f32_try_unsafe); + } + if (env.common.findIdent("Builtin.Num.Dec.to_f64")) |ident| { + try low_level_map.put(ident, .dec_to_f64); + } + // Iterate through all defs and replace matching anno-only defs with low-level implementations + // NOTE: We copy def indices to a separate list first, because operations inside the loop + // may reallocate extra_data, which would invalidate any slice taken from it. const all_defs_slice = env.store.sliceDefs(env.all_defs); var def_indices = std.ArrayList(CIR.Def.Idx).empty; defer def_indices.deinit(gpa); diff --git a/src/build/roc/Builtin.roc b/src/build/roc/Builtin.roc index c679942845..b67311078f 100644 --- a/src/build/roc/Builtin.roc +++ b/src/build/roc/Builtin.roc @@ -294,6 +294,27 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(U16, [OutOfRange]) from_numeral : Numeral -> Try(U16, [InvalidNumeral(Str)]) + + # Conversions to signed integers + to_i8_wrap : U16 -> I8 + to_i8_try : U16 -> Try(I8, [OutOfRange]) + to_i16_wrap : U16 -> I16 + to_i16_try : U16 -> Try(I16, [OutOfRange]) + to_i32 : U16 -> I32 + to_i64 : U16 -> I64 + to_i128 : U16 -> I128 + + # Conversions to unsigned integers + to_u8_wrap : U16 -> U8 + to_u8_try : U16 -> Try(U8, [OutOfRange]) + to_u32 : U16 -> U32 + to_u64 : U16 -> U64 + to_u128 : U16 -> U128 + + # Conversions to floating point (all safe) + to_f32 : U16 -> F32 + to_f64 : U16 -> F64 + to_dec : U16 -> Dec } I16 :: [].{ @@ -317,6 +338,30 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(I16, [OutOfRange]) from_numeral : Numeral -> Try(I16, [InvalidNumeral(Str)]) + + # Conversions to signed integers + to_i8_wrap : I16 -> I8 + to_i8_try : I16 -> Try(I8, [OutOfRange]) + to_i32 : I16 -> I32 + to_i64 : I16 -> I64 + to_i128 : I16 -> I128 + + # Conversions to unsigned integers (all lossy for negative values) + to_u8_wrap : I16 -> U8 + to_u8_try : I16 -> Try(U8, [OutOfRange]) + to_u16_wrap : I16 -> U16 + to_u16_try : I16 -> Try(U16, [OutOfRange]) + to_u32_wrap : I16 -> U32 + to_u32_try : I16 -> Try(U32, [OutOfRange]) + to_u64_wrap : I16 -> U64 + to_u64_try : I16 -> Try(U64, [OutOfRange]) + to_u128_wrap : I16 -> U128 + to_u128_try : I16 -> Try(U128, [OutOfRange]) + + # Conversions to floating point (all safe) + to_f32 : I16 -> F32 + to_f64 : I16 -> F64 + to_dec : I16 -> Dec } U32 :: [].{ @@ -337,6 +382,29 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(U32, [OutOfRange]) from_numeral : Numeral -> Try(U32, [InvalidNumeral(Str)]) + + # Conversions to signed integers + to_i8_wrap : U32 -> I8 + to_i8_try : U32 -> Try(I8, [OutOfRange]) + to_i16_wrap : U32 -> I16 + to_i16_try : U32 -> Try(I16, [OutOfRange]) + to_i32_wrap : U32 -> I32 + to_i32_try : U32 -> Try(I32, [OutOfRange]) + to_i64 : U32 -> I64 + to_i128 : U32 -> I128 + + # Conversions to unsigned integers + to_u8_wrap : U32 -> U8 + to_u8_try : U32 -> Try(U8, [OutOfRange]) + to_u16_wrap : U32 -> U16 + to_u16_try : U32 -> Try(U16, [OutOfRange]) + to_u64 : U32 -> U64 + to_u128 : U32 -> U128 + + # Conversions to floating point (all safe) + to_f32 : U32 -> F32 + to_f64 : U32 -> F64 + to_dec : U32 -> Dec } I32 :: [].{ @@ -360,6 +428,31 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(I32, [OutOfRange]) from_numeral : Numeral -> Try(I32, [InvalidNumeral(Str)]) + + # Conversions to signed integers + to_i8_wrap : I32 -> I8 + to_i8_try : I32 -> Try(I8, [OutOfRange]) + to_i16_wrap : I32 -> I16 + to_i16_try : I32 -> Try(I16, [OutOfRange]) + to_i64 : I32 -> I64 + to_i128 : I32 -> I128 + + # Conversions to unsigned integers (all lossy for negative values) + to_u8_wrap : I32 -> U8 + to_u8_try : I32 -> Try(U8, [OutOfRange]) + to_u16_wrap : I32 -> U16 + to_u16_try : I32 -> Try(U16, [OutOfRange]) + to_u32_wrap : I32 -> U32 + to_u32_try : I32 -> Try(U32, [OutOfRange]) + to_u64_wrap : I32 -> U64 + to_u64_try : I32 -> Try(U64, [OutOfRange]) + to_u128_wrap : I32 -> U128 + to_u128_try : I32 -> Try(U128, [OutOfRange]) + + # Conversions to floating point (all safe) + to_f32 : I32 -> F32 + to_f64 : I32 -> F64 + to_dec : I32 -> Dec } U64 :: [].{ @@ -380,6 +473,31 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(U64, [OutOfRange]) from_numeral : Numeral -> Try(U64, [InvalidNumeral(Str)]) + + # Conversions to signed integers + to_i8_wrap : U64 -> I8 + to_i8_try : U64 -> Try(I8, [OutOfRange]) + to_i16_wrap : U64 -> I16 + to_i16_try : U64 -> Try(I16, [OutOfRange]) + to_i32_wrap : U64 -> I32 + to_i32_try : U64 -> Try(I32, [OutOfRange]) + to_i64_wrap : U64 -> I64 + to_i64_try : U64 -> Try(I64, [OutOfRange]) + to_i128 : U64 -> I128 + + # Conversions to unsigned integers + to_u8_wrap : U64 -> U8 + to_u8_try : U64 -> Try(U8, [OutOfRange]) + to_u16_wrap : U64 -> U16 + to_u16_try : U64 -> Try(U16, [OutOfRange]) + to_u32_wrap : U64 -> U32 + to_u32_try : U64 -> Try(U32, [OutOfRange]) + to_u128 : U64 -> U128 + + # Conversions to floating point (all safe) + to_f32 : U64 -> F32 + to_f64 : U64 -> F64 + to_dec : U64 -> Dec } I64 :: [].{ @@ -403,6 +521,32 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(I64, [OutOfRange]) from_numeral : Numeral -> Try(I64, [InvalidNumeral(Str)]) + + # Conversions to signed integers + to_i8_wrap : I64 -> I8 + to_i8_try : I64 -> Try(I8, [OutOfRange]) + to_i16_wrap : I64 -> I16 + to_i16_try : I64 -> Try(I16, [OutOfRange]) + to_i32_wrap : I64 -> I32 + to_i32_try : I64 -> Try(I32, [OutOfRange]) + to_i128 : I64 -> I128 + + # Conversions to unsigned integers (all lossy for negative values) + to_u8_wrap : I64 -> U8 + to_u8_try : I64 -> Try(U8, [OutOfRange]) + to_u16_wrap : I64 -> U16 + to_u16_try : I64 -> Try(U16, [OutOfRange]) + to_u32_wrap : I64 -> U32 + to_u32_try : I64 -> Try(U32, [OutOfRange]) + to_u64_wrap : I64 -> U64 + to_u64_try : I64 -> Try(U64, [OutOfRange]) + to_u128_wrap : I64 -> U128 + to_u128_try : I64 -> Try(U128, [OutOfRange]) + + # Conversions to floating point (all safe) + to_f32 : I64 -> F32 + to_f64 : I64 -> F64 + to_dec : I64 -> Dec } U128 :: [].{ @@ -423,6 +567,35 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(U128, [OutOfRange]) from_numeral : Numeral -> Try(U128, [InvalidNumeral(Str)]) + + # Conversions to signed integers + to_i8_wrap : U128 -> I8 + to_i8_try : U128 -> Try(I8, [OutOfRange]) + to_i16_wrap : U128 -> I16 + to_i16_try : U128 -> Try(I16, [OutOfRange]) + to_i32_wrap : U128 -> I32 + to_i32_try : U128 -> Try(I32, [OutOfRange]) + to_i64_wrap : U128 -> I64 + to_i64_try : U128 -> Try(I64, [OutOfRange]) + to_i128_wrap : U128 -> I128 + to_i128_try : U128 -> Try(I128, [OutOfRange]) + + # Conversions to unsigned integers + to_u8_wrap : U128 -> U8 + to_u8_try : U128 -> Try(U8, [OutOfRange]) + to_u16_wrap : U128 -> U16 + to_u16_try : U128 -> Try(U16, [OutOfRange]) + to_u32_wrap : U128 -> U32 + to_u32_try : U128 -> Try(U32, [OutOfRange]) + to_u64_wrap : U128 -> U64 + to_u64_try : U128 -> Try(U64, [OutOfRange]) + + # Conversions to floating point (all safe) + to_f32 : U128 -> F32 + to_f64 : U128 -> F64 + + # Conversion to Dec (can overflow) + to_dec_try : U128 -> Try(Dec, [OutOfRange]) } I128 :: [].{ @@ -446,6 +619,35 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(I128, [OutOfRange]) from_numeral : Numeral -> Try(I128, [InvalidNumeral(Str)]) + + # Conversions to signed integers + to_i8_wrap : I128 -> I8 + to_i8_try : I128 -> Try(I8, [OutOfRange]) + to_i16_wrap : I128 -> I16 + to_i16_try : I128 -> Try(I16, [OutOfRange]) + to_i32_wrap : I128 -> I32 + to_i32_try : I128 -> Try(I32, [OutOfRange]) + to_i64_wrap : I128 -> I64 + to_i64_try : I128 -> Try(I64, [OutOfRange]) + + # Conversions to unsigned integers (all lossy for negative values) + to_u8_wrap : I128 -> U8 + to_u8_try : I128 -> Try(U8, [OutOfRange]) + to_u16_wrap : I128 -> U16 + to_u16_try : I128 -> Try(U16, [OutOfRange]) + to_u32_wrap : I128 -> U32 + to_u32_try : I128 -> Try(U32, [OutOfRange]) + to_u64_wrap : I128 -> U64 + to_u64_try : I128 -> Try(U64, [OutOfRange]) + to_u128_wrap : I128 -> U128 + to_u128_try : I128 -> Try(U128, [OutOfRange]) + + # Conversions to floating point (all safe) + to_f32 : I128 -> F32 + to_f64 : I128 -> F64 + + # Conversion to Dec (can overflow) + to_dec_try : I128 -> Try(Dec, [OutOfRange]) } Dec :: [].{ @@ -470,6 +672,35 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(Dec, [OutOfRange]) from_dec_digits : (List(U8), List(U8)) -> Try(Dec, [OutOfRange]) from_numeral : Numeral -> Try(Dec, [InvalidNumeral(Str)]) + + # Conversions to signed integers (all lossy - truncates fractional part) + to_i8_wrap : Dec -> I8 + to_i8_try : Dec -> Try(I8, [OutOfRange]) + to_i16_wrap : Dec -> I16 + to_i16_try : Dec -> Try(I16, [OutOfRange]) + to_i32_wrap : Dec -> I32 + to_i32_try : Dec -> Try(I32, [OutOfRange]) + to_i64_wrap : Dec -> I64 + to_i64_try : Dec -> Try(I64, [OutOfRange]) + to_i128_wrap : Dec -> I128 + to_i128_try : Dec -> Try(I128, [OutOfRange]) + + # Conversions to unsigned integers (all lossy - truncates fractional part) + to_u8_wrap : Dec -> U8 + to_u8_try : Dec -> Try(U8, [OutOfRange]) + to_u16_wrap : Dec -> U16 + to_u16_try : Dec -> Try(U16, [OutOfRange]) + to_u32_wrap : Dec -> U32 + to_u32_try : Dec -> Try(U32, [OutOfRange]) + to_u64_wrap : Dec -> U64 + to_u64_try : Dec -> Try(U64, [OutOfRange]) + to_u128_wrap : Dec -> U128 + to_u128_try : Dec -> Try(U128, [OutOfRange]) + + # Conversions to floating point (lossy - Dec has more precision) + to_f32_wrap : Dec -> F32 + to_f32_try : Dec -> Try(F32, [OutOfRange]) + to_f64 : Dec -> F64 } F32 :: [].{ @@ -493,6 +724,33 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(F32, [OutOfRange]) from_dec_digits : (List(U8), List(U8)) -> Try(F32, [OutOfRange]) from_numeral : Numeral -> Try(F32, [InvalidNumeral(Str)]) + + # Conversions to signed integers (all lossy - truncation + range check) + to_i8_wrap : F32 -> I8 + to_i8_try : F32 -> Try(I8, [OutOfRange]) + to_i16_wrap : F32 -> I16 + to_i16_try : F32 -> Try(I16, [OutOfRange]) + to_i32_wrap : F32 -> I32 + to_i32_try : F32 -> Try(I32, [OutOfRange]) + to_i64_wrap : F32 -> I64 + to_i64_try : F32 -> Try(I64, [OutOfRange]) + to_i128_wrap : F32 -> I128 + to_i128_try : F32 -> Try(I128, [OutOfRange]) + + # Conversions to unsigned integers (all lossy - truncation + range check) + to_u8_wrap : F32 -> U8 + to_u8_try : F32 -> Try(U8, [OutOfRange]) + to_u16_wrap : F32 -> U16 + to_u16_try : F32 -> Try(U16, [OutOfRange]) + to_u32_wrap : F32 -> U32 + to_u32_try : F32 -> Try(U32, [OutOfRange]) + to_u64_wrap : F32 -> U64 + to_u64_try : F32 -> Try(U64, [OutOfRange]) + to_u128_wrap : F32 -> U128 + to_u128_try : F32 -> Try(U128, [OutOfRange]) + + # Conversion to F64 (safe widening) + to_f64 : F32 -> F64 } F64 :: [].{ @@ -516,6 +774,39 @@ Builtin :: [].{ from_int_digits : List(U8) -> Try(F64, [OutOfRange]) from_dec_digits : (List(U8), List(U8)) -> Try(F64, [OutOfRange]) from_numeral : Numeral -> Try(F64, [InvalidNumeral(Str)]) + + # Conversions to signed integers (all lossy - truncation + range check) + to_i8_wrap : F64 -> I8 + to_i8_try : F64 -> Try(I8, [OutOfRange]) + to_i16_wrap : F64 -> I16 + to_i16_try : F64 -> Try(I16, [OutOfRange]) + to_i32_wrap : F64 -> I32 + to_i32_try : F64 -> Try(I32, [OutOfRange]) + to_i64_wrap : F64 -> I64 + to_i64_try : F64 -> Try(I64, [OutOfRange]) + to_i128_wrap : F64 -> I128 + to_i128_try : F64 -> Try(I128, [OutOfRange]) + + # Conversions to unsigned integers (all lossy - truncation + range check) + to_u8_wrap : F64 -> U8 + to_u8_try : F64 -> Try(U8, [OutOfRange]) + to_u16_wrap : F64 -> U16 + to_u16_try : F64 -> Try(U16, [OutOfRange]) + to_u32_wrap : F64 -> U32 + to_u32_try : F64 -> Try(U32, [OutOfRange]) + to_u64_wrap : F64 -> U64 + to_u64_try : F64 -> Try(U64, [OutOfRange]) + to_u128_wrap : F64 -> U128 + to_u128_try : F64 -> Try(U128, [OutOfRange]) + + # Conversion to F32 (lossy narrowing) + to_f32_wrap : F64 -> F32 + + to_f32_try : F64 -> Try(F32, [OutOfRange]) + to_f32_try = |num| { + answer = f64_to_f32_try_unsafe(num) + if answer.success != 0 { Ok(answer.val_or_memory_garbage) } else { Err(OutOfRange) } + } } } } @@ -523,3 +814,8 @@ Builtin :: [].{ # Private top-level function for unsafe list access # This is a low-level operation that gets replaced by the compiler list_get_unsafe : List(item), U64 -> item + +# Unsafe conversion functions - these return simple records instead of Try types +# They are low-level operations that get replaced by the compiler +# Note: success is U8 (0 = false, 1 = true) since Bool is not available at top level +f64_to_f32_try_unsafe : F64 -> { success : U8, val_or_memory_garbage : F32 } diff --git a/src/builtins/dec.zig b/src/builtins/dec.zig index 0de142c989..8e09a54c3a 100644 --- a/src/builtins/dec.zig +++ b/src/builtins/dec.zig @@ -1058,6 +1058,48 @@ pub fn toF64(arg: RocDec) callconv(.c) f64 { return @call(.always_inline, RocDec.toF64, .{arg}); } +/// Convert Dec to F32 (lossy conversion) +pub fn toF32(arg: RocDec) callconv(.c) f32 { + return @floatCast(arg.toF64()); +} + +/// Convert Dec to F32 with range check - returns null if out of range +pub fn toF32Try(arg: RocDec) ?f32 { + const f64_val = arg.toF64(); + // Check if the value is within F32 range + if (f64_val > math.floatMax(f32) or f64_val < -math.floatMax(f32)) { + return null; + } + // Also check for infinity (which would indicate overflow) + const f32_val: f32 = @floatCast(f64_val); + if (math.isInf(f32_val) and !math.isInf(f64_val)) { + return null; + } + return f32_val; +} + +/// Convert Dec to integer by truncating the fractional part (wrapping on overflow) +pub fn toIntWrap(comptime T: type, arg: RocDec) T { + // Divide by one_point_zero_i128 to get the integer part + const whole_part = @divTrunc(arg.num, RocDec.one_point_zero_i128); + // Truncate to the target type (wrapping) + // First cast the i128 to u128, then truncate to the target size, then cast back to T if needed + const as_u128: u128 = @bitCast(whole_part); + const truncated = @as(std.meta.Int(.unsigned, @bitSizeOf(T)), @truncate(as_u128)); + return @bitCast(truncated); +} + +/// Convert Dec to integer by truncating the fractional part (returns null if out of range) +pub fn toIntTry(comptime T: type, arg: RocDec) ?T { + // Divide by one_point_zero_i128 to get the integer part + const whole_part = @divTrunc(arg.num, RocDec.one_point_zero_i128); + // Check if it fits in the target type + if (whole_part < math.minInt(T) or whole_part > math.maxInt(T)) { + return null; + } + return @intCast(whole_part); +} + /// TODO: Document exportFromInt. pub fn exportFromInt(comptime T: type, comptime name: []const u8) void { const f = struct { diff --git a/src/canonicalize/Expression.zig b/src/canonicalize/Expression.zig index 59de015a69..75fc1e53f2 100644 --- a/src/canonicalize/Expression.zig +++ b/src/canonicalize/Expression.zig @@ -536,6 +536,244 @@ pub const Expr = union(enum) { i8_to_f32, // I8 -> F32 (safe) i8_to_f64, // I8 -> F64 (safe) i8_to_dec, // I8 -> Dec (safe) + + // Numeric conversion operations (U16) + u16_to_i8_wrap, // U16 -> I8 (wrapping) + u16_to_i8_try, // U16 -> Try(I8, [OutOfRange]) + u16_to_i16_wrap, // U16 -> I16 (wrapping) + u16_to_i16_try, // U16 -> Try(I16, [OutOfRange]) + u16_to_i32, // U16 -> I32 (safe) + u16_to_i64, // U16 -> I64 (safe) + u16_to_i128, // U16 -> I128 (safe) + u16_to_u8_wrap, // U16 -> U8 (wrapping) + u16_to_u8_try, // U16 -> Try(U8, [OutOfRange]) + u16_to_u32, // U16 -> U32 (safe) + u16_to_u64, // U16 -> U64 (safe) + u16_to_u128, // U16 -> U128 (safe) + u16_to_f32, // U16 -> F32 (safe) + u16_to_f64, // U16 -> F64 (safe) + u16_to_dec, // U16 -> Dec (safe) + + // Numeric conversion operations (I16) + i16_to_i8_wrap, // I16 -> I8 (wrapping) + i16_to_i8_try, // I16 -> Try(I8, [OutOfRange]) + i16_to_i32, // I16 -> I32 (safe) + i16_to_i64, // I16 -> I64 (safe) + i16_to_i128, // I16 -> I128 (safe) + i16_to_u8_wrap, // I16 -> U8 (wrapping) + i16_to_u8_try, // I16 -> Try(U8, [OutOfRange]) + i16_to_u16_wrap, // I16 -> U16 (wrapping) + i16_to_u16_try, // I16 -> Try(U16, [OutOfRange]) + i16_to_u32_wrap, // I16 -> U32 (wrapping) + i16_to_u32_try, // I16 -> Try(U32, [OutOfRange]) + i16_to_u64_wrap, // I16 -> U64 (wrapping) + i16_to_u64_try, // I16 -> Try(U64, [OutOfRange]) + i16_to_u128_wrap, // I16 -> U128 (wrapping) + i16_to_u128_try, // I16 -> Try(U128, [OutOfRange]) + i16_to_f32, // I16 -> F32 (safe) + i16_to_f64, // I16 -> F64 (safe) + i16_to_dec, // I16 -> Dec (safe) + + // Numeric conversion operations (U32) + u32_to_i8_wrap, // U32 -> I8 (wrapping) + u32_to_i8_try, // U32 -> Try(I8, [OutOfRange]) + u32_to_i16_wrap, // U32 -> I16 (wrapping) + u32_to_i16_try, // U32 -> Try(I16, [OutOfRange]) + u32_to_i32_wrap, // U32 -> I32 (wrapping) + u32_to_i32_try, // U32 -> Try(I32, [OutOfRange]) + u32_to_i64, // U32 -> I64 (safe) + u32_to_i128, // U32 -> I128 (safe) + u32_to_u8_wrap, // U32 -> U8 (wrapping) + u32_to_u8_try, // U32 -> Try(U8, [OutOfRange]) + u32_to_u16_wrap, // U32 -> U16 (wrapping) + u32_to_u16_try, // U32 -> Try(U16, [OutOfRange]) + u32_to_u64, // U32 -> U64 (safe) + u32_to_u128, // U32 -> U128 (safe) + u32_to_f32, // U32 -> F32 (safe) + u32_to_f64, // U32 -> F64 (safe) + u32_to_dec, // U32 -> Dec (safe) + + // Numeric conversion operations (I32) + i32_to_i8_wrap, // I32 -> I8 (wrapping) + i32_to_i8_try, // I32 -> Try(I8, [OutOfRange]) + i32_to_i16_wrap, // I32 -> I16 (wrapping) + i32_to_i16_try, // I32 -> Try(I16, [OutOfRange]) + i32_to_i64, // I32 -> I64 (safe) + i32_to_i128, // I32 -> I128 (safe) + i32_to_u8_wrap, // I32 -> U8 (wrapping) + i32_to_u8_try, // I32 -> Try(U8, [OutOfRange]) + i32_to_u16_wrap, // I32 -> U16 (wrapping) + i32_to_u16_try, // I32 -> Try(U16, [OutOfRange]) + i32_to_u32_wrap, // I32 -> U32 (wrapping) + i32_to_u32_try, // I32 -> Try(U32, [OutOfRange]) + i32_to_u64_wrap, // I32 -> U64 (wrapping) + i32_to_u64_try, // I32 -> Try(U64, [OutOfRange]) + i32_to_u128_wrap, // I32 -> U128 (wrapping) + i32_to_u128_try, // I32 -> Try(U128, [OutOfRange]) + i32_to_f32, // I32 -> F32 (safe) + i32_to_f64, // I32 -> F64 (safe) + i32_to_dec, // I32 -> Dec (safe) + + // Numeric conversion operations (U64) + u64_to_i8_wrap, // U64 -> I8 (wrapping) + u64_to_i8_try, // U64 -> Try(I8, [OutOfRange]) + u64_to_i16_wrap, // U64 -> I16 (wrapping) + u64_to_i16_try, // U64 -> Try(I16, [OutOfRange]) + u64_to_i32_wrap, // U64 -> I32 (wrapping) + u64_to_i32_try, // U64 -> Try(I32, [OutOfRange]) + u64_to_i64_wrap, // U64 -> I64 (wrapping) + u64_to_i64_try, // U64 -> Try(I64, [OutOfRange]) + u64_to_i128, // U64 -> I128 (safe) + u64_to_u8_wrap, // U64 -> U8 (wrapping) + u64_to_u8_try, // U64 -> Try(U8, [OutOfRange]) + u64_to_u16_wrap, // U64 -> U16 (wrapping) + u64_to_u16_try, // U64 -> Try(U16, [OutOfRange]) + u64_to_u32_wrap, // U64 -> U32 (wrapping) + u64_to_u32_try, // U64 -> Try(U32, [OutOfRange]) + u64_to_u128, // U64 -> U128 (safe) + u64_to_f32, // U64 -> F32 (safe) + u64_to_f64, // U64 -> F64 (safe) + u64_to_dec, // U64 -> Dec (safe) + + // Numeric conversion operations (I64) + i64_to_i8_wrap, // I64 -> I8 (wrapping) + i64_to_i8_try, // I64 -> Try(I8, [OutOfRange]) + i64_to_i16_wrap, // I64 -> I16 (wrapping) + i64_to_i16_try, // I64 -> Try(I16, [OutOfRange]) + i64_to_i32_wrap, // I64 -> I32 (wrapping) + i64_to_i32_try, // I64 -> Try(I32, [OutOfRange]) + i64_to_i128, // I64 -> I128 (safe) + i64_to_u8_wrap, // I64 -> U8 (wrapping) + i64_to_u8_try, // I64 -> Try(U8, [OutOfRange]) + i64_to_u16_wrap, // I64 -> U16 (wrapping) + i64_to_u16_try, // I64 -> Try(U16, [OutOfRange]) + i64_to_u32_wrap, // I64 -> U32 (wrapping) + i64_to_u32_try, // I64 -> Try(U32, [OutOfRange]) + i64_to_u64_wrap, // I64 -> U64 (wrapping) + i64_to_u64_try, // I64 -> Try(U64, [OutOfRange]) + i64_to_u128_wrap, // I64 -> U128 (wrapping) + i64_to_u128_try, // I64 -> Try(U128, [OutOfRange]) + i64_to_f32, // I64 -> F32 (safe) + i64_to_f64, // I64 -> F64 (safe) + i64_to_dec, // I64 -> Dec (safe) + + // Numeric conversion operations (U128) + u128_to_i8_wrap, // U128 -> I8 (wrapping) + u128_to_i8_try, // U128 -> Try(I8, [OutOfRange]) + u128_to_i16_wrap, // U128 -> I16 (wrapping) + u128_to_i16_try, // U128 -> Try(I16, [OutOfRange]) + u128_to_i32_wrap, // U128 -> I32 (wrapping) + u128_to_i32_try, // U128 -> Try(I32, [OutOfRange]) + u128_to_i64_wrap, // U128 -> I64 (wrapping) + u128_to_i64_try, // U128 -> Try(I64, [OutOfRange]) + u128_to_i128_wrap, // U128 -> I128 (wrapping) + u128_to_i128_try, // U128 -> Try(I128, [OutOfRange]) + u128_to_u8_wrap, // U128 -> U8 (wrapping) + u128_to_u8_try, // U128 -> Try(U8, [OutOfRange]) + u128_to_u16_wrap, // U128 -> U16 (wrapping) + u128_to_u16_try, // U128 -> Try(U16, [OutOfRange]) + u128_to_u32_wrap, // U128 -> U32 (wrapping) + u128_to_u32_try, // U128 -> Try(U32, [OutOfRange]) + u128_to_u64_wrap, // U128 -> U64 (wrapping) + u128_to_u64_try, // U128 -> Try(U64, [OutOfRange]) + u128_to_f32, // U128 -> F32 (safe) + u128_to_f64, // U128 -> F64 (safe) + u128_to_dec_try_unsafe, // U128 -> { success: Bool, val: Dec } + + // Numeric conversion operations (I128) + i128_to_i8_wrap, // I128 -> I8 (wrapping) + i128_to_i8_try, // I128 -> Try(I8, [OutOfRange]) + i128_to_i16_wrap, // I128 -> I16 (wrapping) + i128_to_i16_try, // I128 -> Try(I16, [OutOfRange]) + i128_to_i32_wrap, // I128 -> I32 (wrapping) + i128_to_i32_try, // I128 -> Try(I32, [OutOfRange]) + i128_to_i64_wrap, // I128 -> I64 (wrapping) + i128_to_i64_try, // I128 -> Try(I64, [OutOfRange]) + i128_to_u8_wrap, // I128 -> U8 (wrapping) + i128_to_u8_try, // I128 -> Try(U8, [OutOfRange]) + i128_to_u16_wrap, // I128 -> U16 (wrapping) + i128_to_u16_try, // I128 -> Try(U16, [OutOfRange]) + i128_to_u32_wrap, // I128 -> U32 (wrapping) + i128_to_u32_try, // I128 -> Try(U32, [OutOfRange]) + i128_to_u64_wrap, // I128 -> U64 (wrapping) + i128_to_u64_try, // I128 -> Try(U64, [OutOfRange]) + i128_to_u128_wrap, // I128 -> U128 (wrapping) + i128_to_u128_try, // I128 -> Try(U128, [OutOfRange]) + i128_to_f32, // I128 -> F32 (safe) + i128_to_f64, // I128 -> F64 (safe) + i128_to_dec_try_unsafe, // I128 -> { success: Bool, val: Dec } + + // Numeric conversion operations (F32) + f32_to_i8_trunc, // F32 -> I8 (truncating) + f32_to_i8_try_unsafe, // F32 -> { is_int: Bool, in_range: Bool, val: I8 } + f32_to_i16_trunc, // F32 -> I16 (truncating) + f32_to_i16_try_unsafe, // F32 -> { is_int: Bool, in_range: Bool, val: I16 } + f32_to_i32_trunc, // F32 -> I32 (truncating) + f32_to_i32_try_unsafe, // F32 -> { is_int: Bool, in_range: Bool, val: I32 } + f32_to_i64_trunc, // F32 -> I64 (truncating) + f32_to_i64_try_unsafe, // F32 -> { is_int: Bool, in_range: Bool, val: I64 } + f32_to_i128_trunc, // F32 -> I128 (truncating) + f32_to_i128_try_unsafe, // F32 -> { is_int: Bool, in_range: Bool, val: I128 } + f32_to_u8_trunc, // F32 -> U8 (truncating) + f32_to_u8_try_unsafe, // F32 -> { is_int: Bool, in_range: Bool, val: U8 } + f32_to_u16_trunc, // F32 -> U16 (truncating) + f32_to_u16_try_unsafe, // F32 -> { is_int: Bool, in_range: Bool, val: U16 } + f32_to_u32_trunc, // F32 -> U32 (truncating) + f32_to_u32_try_unsafe, // F32 -> { is_int: Bool, in_range: Bool, val: U32 } + f32_to_u64_trunc, // F32 -> U64 (truncating) + f32_to_u64_try_unsafe, // F32 -> { is_int: Bool, in_range: Bool, val: U64 } + f32_to_u128_trunc, // F32 -> U128 (truncating) + f32_to_u128_try_unsafe, // F32 -> { is_int: Bool, in_range: Bool, val: U128 } + f32_to_f64, // F32 -> F64 (safe widening) + + // Numeric conversion operations (F64) + f64_to_i8_trunc, // F64 -> I8 (truncating) + f64_to_i8_try_unsafe, // F64 -> { is_int: Bool, in_range: Bool, val: I8 } + f64_to_i16_trunc, // F64 -> I16 (truncating) + f64_to_i16_try_unsafe, // F64 -> { is_int: Bool, in_range: Bool, val: I16 } + f64_to_i32_trunc, // F64 -> I32 (truncating) + f64_to_i32_try_unsafe, // F64 -> { is_int: Bool, in_range: Bool, val: I32 } + f64_to_i64_trunc, // F64 -> I64 (truncating) + f64_to_i64_try_unsafe, // F64 -> { is_int: Bool, in_range: Bool, val: I64 } + f64_to_i128_trunc, // F64 -> I128 (truncating) + f64_to_i128_try_unsafe, // F64 -> { is_int: Bool, in_range: Bool, val: I128 } + f64_to_u8_trunc, // F64 -> U8 (truncating) + f64_to_u8_try_unsafe, // F64 -> { is_int: Bool, in_range: Bool, val: U8 } + f64_to_u16_trunc, // F64 -> U16 (truncating) + f64_to_u16_try_unsafe, // F64 -> { is_int: Bool, in_range: Bool, val: U16 } + f64_to_u32_trunc, // F64 -> U32 (truncating) + f64_to_u32_try_unsafe, // F64 -> { is_int: Bool, in_range: Bool, val: U32 } + f64_to_u64_trunc, // F64 -> U64 (truncating) + f64_to_u64_try_unsafe, // F64 -> { is_int: Bool, in_range: Bool, val: U64 } + f64_to_u128_trunc, // F64 -> U128 (truncating) + f64_to_u128_try_unsafe, // F64 -> { is_int: Bool, in_range: Bool, val: U128 } + f64_to_f32_wrap, // F64 -> F32 (lossy narrowing) + f64_to_f32_try_unsafe, // F64 -> { success: Bool, val: F32 } + + // Numeric conversion operations (Dec) + dec_to_i8_trunc, // Dec -> I8 (truncating) + dec_to_i8_try_unsafe, // Dec -> { is_int: Bool, in_range: Bool, val: I8 } + dec_to_i16_trunc, // Dec -> I16 (truncating) + dec_to_i16_try_unsafe, // Dec -> { is_int: Bool, in_range: Bool, val: I16 } + dec_to_i32_trunc, // Dec -> I32 (truncating) + dec_to_i32_try_unsafe, // Dec -> { is_int: Bool, in_range: Bool, val: I32 } + dec_to_i64_trunc, // Dec -> I64 (truncating) + dec_to_i64_try_unsafe, // Dec -> { is_int: Bool, in_range: Bool, val: I64 } + dec_to_i128_trunc, // Dec -> I128 (truncating) + dec_to_i128_try_unsafe, // Dec -> { is_int: Bool, val: I128 } - always in range + dec_to_u8_trunc, // Dec -> U8 (truncating) + dec_to_u8_try_unsafe, // Dec -> { is_int: Bool, in_range: Bool, val: U8 } + dec_to_u16_trunc, // Dec -> U16 (truncating) + dec_to_u16_try_unsafe, // Dec -> { is_int: Bool, in_range: Bool, val: U16 } + dec_to_u32_trunc, // Dec -> U32 (truncating) + dec_to_u32_try_unsafe, // Dec -> { is_int: Bool, in_range: Bool, val: U32 } + dec_to_u64_trunc, // Dec -> U64 (truncating) + dec_to_u64_try_unsafe, // Dec -> { is_int: Bool, in_range: Bool, val: U64 } + dec_to_u128_trunc, // Dec -> U128 (truncating) + dec_to_u128_try_unsafe, // Dec -> { is_int: Bool, in_range: Bool, val: U128 } + dec_to_f32_wrap, // Dec -> F32 (lossy narrowing) + dec_to_f32_try_unsafe, // Dec -> { success: Bool, val: F32 } + dec_to_f64, // Dec -> F64 (lossy conversion) }; pub const Idx = enum(u32) { _ }; diff --git a/src/eval/interpreter.zig b/src/eval/interpreter.zig index d996699bbe..38d5e49c44 100644 --- a/src/eval/interpreter.zig +++ b/src/eval/interpreter.zig @@ -570,6 +570,23 @@ pub const Interpreter = struct { return StackValue{ .layout = layout_val, .ptr = ptr, .is_initialized = true }; } + /// Push raw bytes with a specific size and alignment (for building records/tuples) + pub fn pushRawBytes(self: *Interpreter, size: usize, alignment: usize) !StackValue { + if (size == 0) { + return StackValue{ .layout = .{ .tag = .zst, .data = undefined }, .ptr = null, .is_initialized = true }; + } + const align_enum: std.mem.Alignment = switch (alignment) { + 1 => .@"1", + 2 => .@"2", + 4 => .@"4", + 8 => .@"8", + 16 => .@"16", + else => .@"1", + }; + const ptr = try self.stack_memory.alloca(@intCast(size), align_enum); + return StackValue{ .layout = .{ .tag = .zst, .data = undefined }, .ptr = ptr, .is_initialized = true }; + } + pub fn pushCopy(self: *Interpreter, src: StackValue, roc_ops: *RocOps) !StackValue { const size: u32 = if (src.layout.tag == .closure) src.getTotalSize(&self.runtime_layout_store) else self.runtime_layout_store.layoutSize(src.layout); const target_usize = self.runtime_layout_store.targetUsize(); @@ -2508,6 +2525,247 @@ pub const Interpreter = struct { .i8_to_f32 => return self.intToFloat(i8, f32, args), .i8_to_f64 => return self.intToFloat(i8, f64, args), .i8_to_dec => return self.intToDec(i8, args), + + // U16 conversion operations + .u16_to_i8_wrap => return self.intConvertWrap(u16, i8, args), + .u16_to_i8_try => return self.intConvertTry(u16, i8, args, return_rt_var), + .u16_to_i16_wrap => return self.intConvertWrap(u16, i16, args), + .u16_to_i16_try => return self.intConvertTry(u16, i16, args, return_rt_var), + .u16_to_i32 => return self.intConvert(u16, i32, args), + .u16_to_i64 => return self.intConvert(u16, i64, args), + .u16_to_i128 => return self.intConvert(u16, i128, args), + .u16_to_u8_wrap => return self.intConvertWrap(u16, u8, args), + .u16_to_u8_try => return self.intConvertTry(u16, u8, args, return_rt_var), + .u16_to_u32 => return self.intConvert(u16, u32, args), + .u16_to_u64 => return self.intConvert(u16, u64, args), + .u16_to_u128 => return self.intConvert(u16, u128, args), + .u16_to_f32 => return self.intToFloat(u16, f32, args), + .u16_to_f64 => return self.intToFloat(u16, f64, args), + .u16_to_dec => return self.intToDec(u16, args), + + // I16 conversion operations + .i16_to_i8_wrap => return self.intConvertWrap(i16, i8, args), + .i16_to_i8_try => return self.intConvertTry(i16, i8, args, return_rt_var), + .i16_to_i32 => return self.intConvert(i16, i32, args), + .i16_to_i64 => return self.intConvert(i16, i64, args), + .i16_to_i128 => return self.intConvert(i16, i128, args), + .i16_to_u8_wrap => return self.intConvertWrap(i16, u8, args), + .i16_to_u8_try => return self.intConvertTry(i16, u8, args, return_rt_var), + .i16_to_u16_wrap => return self.intConvertWrap(i16, u16, args), + .i16_to_u16_try => return self.intConvertTry(i16, u16, args, return_rt_var), + .i16_to_u32_wrap => return self.intConvertWrap(i16, u32, args), + .i16_to_u32_try => return self.intConvertTry(i16, u32, args, return_rt_var), + .i16_to_u64_wrap => return self.intConvertWrap(i16, u64, args), + .i16_to_u64_try => return self.intConvertTry(i16, u64, args, return_rt_var), + .i16_to_u128_wrap => return self.intConvertWrap(i16, u128, args), + .i16_to_u128_try => return self.intConvertTry(i16, u128, args, return_rt_var), + .i16_to_f32 => return self.intToFloat(i16, f32, args), + .i16_to_f64 => return self.intToFloat(i16, f64, args), + .i16_to_dec => return self.intToDec(i16, args), + + // U32 conversion operations + .u32_to_i8_wrap => return self.intConvertWrap(u32, i8, args), + .u32_to_i8_try => return self.intConvertTry(u32, i8, args, return_rt_var), + .u32_to_i16_wrap => return self.intConvertWrap(u32, i16, args), + .u32_to_i16_try => return self.intConvertTry(u32, i16, args, return_rt_var), + .u32_to_i32_wrap => return self.intConvertWrap(u32, i32, args), + .u32_to_i32_try => return self.intConvertTry(u32, i32, args, return_rt_var), + .u32_to_i64 => return self.intConvert(u32, i64, args), + .u32_to_i128 => return self.intConvert(u32, i128, args), + .u32_to_u8_wrap => return self.intConvertWrap(u32, u8, args), + .u32_to_u8_try => return self.intConvertTry(u32, u8, args, return_rt_var), + .u32_to_u16_wrap => return self.intConvertWrap(u32, u16, args), + .u32_to_u16_try => return self.intConvertTry(u32, u16, args, return_rt_var), + .u32_to_u64 => return self.intConvert(u32, u64, args), + .u32_to_u128 => return self.intConvert(u32, u128, args), + .u32_to_f32 => return self.intToFloat(u32, f32, args), + .u32_to_f64 => return self.intToFloat(u32, f64, args), + .u32_to_dec => return self.intToDec(u32, args), + + // I32 conversion operations + .i32_to_i8_wrap => return self.intConvertWrap(i32, i8, args), + .i32_to_i8_try => return self.intConvertTry(i32, i8, args, return_rt_var), + .i32_to_i16_wrap => return self.intConvertWrap(i32, i16, args), + .i32_to_i16_try => return self.intConvertTry(i32, i16, args, return_rt_var), + .i32_to_i64 => return self.intConvert(i32, i64, args), + .i32_to_i128 => return self.intConvert(i32, i128, args), + .i32_to_u8_wrap => return self.intConvertWrap(i32, u8, args), + .i32_to_u8_try => return self.intConvertTry(i32, u8, args, return_rt_var), + .i32_to_u16_wrap => return self.intConvertWrap(i32, u16, args), + .i32_to_u16_try => return self.intConvertTry(i32, u16, args, return_rt_var), + .i32_to_u32_wrap => return self.intConvertWrap(i32, u32, args), + .i32_to_u32_try => return self.intConvertTry(i32, u32, args, return_rt_var), + .i32_to_u64_wrap => return self.intConvertWrap(i32, u64, args), + .i32_to_u64_try => return self.intConvertTry(i32, u64, args, return_rt_var), + .i32_to_u128_wrap => return self.intConvertWrap(i32, u128, args), + .i32_to_u128_try => return self.intConvertTry(i32, u128, args, return_rt_var), + .i32_to_f32 => return self.intToFloat(i32, f32, args), + .i32_to_f64 => return self.intToFloat(i32, f64, args), + .i32_to_dec => return self.intToDec(i32, args), + + // U64 conversion operations + .u64_to_i8_wrap => return self.intConvertWrap(u64, i8, args), + .u64_to_i8_try => return self.intConvertTry(u64, i8, args, return_rt_var), + .u64_to_i16_wrap => return self.intConvertWrap(u64, i16, args), + .u64_to_i16_try => return self.intConvertTry(u64, i16, args, return_rt_var), + .u64_to_i32_wrap => return self.intConvertWrap(u64, i32, args), + .u64_to_i32_try => return self.intConvertTry(u64, i32, args, return_rt_var), + .u64_to_i64_wrap => return self.intConvertWrap(u64, i64, args), + .u64_to_i64_try => return self.intConvertTry(u64, i64, args, return_rt_var), + .u64_to_i128 => return self.intConvert(u64, i128, args), + .u64_to_u8_wrap => return self.intConvertWrap(u64, u8, args), + .u64_to_u8_try => return self.intConvertTry(u64, u8, args, return_rt_var), + .u64_to_u16_wrap => return self.intConvertWrap(u64, u16, args), + .u64_to_u16_try => return self.intConvertTry(u64, u16, args, return_rt_var), + .u64_to_u32_wrap => return self.intConvertWrap(u64, u32, args), + .u64_to_u32_try => return self.intConvertTry(u64, u32, args, return_rt_var), + .u64_to_u128 => return self.intConvert(u64, u128, args), + .u64_to_f32 => return self.intToFloat(u64, f32, args), + .u64_to_f64 => return self.intToFloat(u64, f64, args), + .u64_to_dec => return self.intToDec(u64, args), + + // I64 conversion operations + .i64_to_i8_wrap => return self.intConvertWrap(i64, i8, args), + .i64_to_i8_try => return self.intConvertTry(i64, i8, args, return_rt_var), + .i64_to_i16_wrap => return self.intConvertWrap(i64, i16, args), + .i64_to_i16_try => return self.intConvertTry(i64, i16, args, return_rt_var), + .i64_to_i32_wrap => return self.intConvertWrap(i64, i32, args), + .i64_to_i32_try => return self.intConvertTry(i64, i32, args, return_rt_var), + .i64_to_i128 => return self.intConvert(i64, i128, args), + .i64_to_u8_wrap => return self.intConvertWrap(i64, u8, args), + .i64_to_u8_try => return self.intConvertTry(i64, u8, args, return_rt_var), + .i64_to_u16_wrap => return self.intConvertWrap(i64, u16, args), + .i64_to_u16_try => return self.intConvertTry(i64, u16, args, return_rt_var), + .i64_to_u32_wrap => return self.intConvertWrap(i64, u32, args), + .i64_to_u32_try => return self.intConvertTry(i64, u32, args, return_rt_var), + .i64_to_u64_wrap => return self.intConvertWrap(i64, u64, args), + .i64_to_u64_try => return self.intConvertTry(i64, u64, args, return_rt_var), + .i64_to_u128_wrap => return self.intConvertWrap(i64, u128, args), + .i64_to_u128_try => return self.intConvertTry(i64, u128, args, return_rt_var), + .i64_to_f32 => return self.intToFloat(i64, f32, args), + .i64_to_f64 => return self.intToFloat(i64, f64, args), + .i64_to_dec => return self.intToDec(i64, args), + + // U128 conversion operations + .u128_to_i8_wrap => return self.intConvertWrap(u128, i8, args), + .u128_to_i8_try => return self.intConvertTry(u128, i8, args, return_rt_var), + .u128_to_i16_wrap => return self.intConvertWrap(u128, i16, args), + .u128_to_i16_try => return self.intConvertTry(u128, i16, args, return_rt_var), + .u128_to_i32_wrap => return self.intConvertWrap(u128, i32, args), + .u128_to_i32_try => return self.intConvertTry(u128, i32, args, return_rt_var), + .u128_to_i64_wrap => return self.intConvertWrap(u128, i64, args), + .u128_to_i64_try => return self.intConvertTry(u128, i64, args, return_rt_var), + .u128_to_i128_wrap => return self.intConvertWrap(u128, i128, args), + .u128_to_i128_try => return self.intConvertTry(u128, i128, args, return_rt_var), + .u128_to_u8_wrap => return self.intConvertWrap(u128, u8, args), + .u128_to_u8_try => return self.intConvertTry(u128, u8, args, return_rt_var), + .u128_to_u16_wrap => return self.intConvertWrap(u128, u16, args), + .u128_to_u16_try => return self.intConvertTry(u128, u16, args, return_rt_var), + .u128_to_u32_wrap => return self.intConvertWrap(u128, u32, args), + .u128_to_u32_try => return self.intConvertTry(u128, u32, args, return_rt_var), + .u128_to_u64_wrap => return self.intConvertWrap(u128, u64, args), + .u128_to_u64_try => return self.intConvertTry(u128, u64, args, return_rt_var), + .u128_to_f32 => return self.intToFloat(u128, f32, args), + .u128_to_f64 => return self.intToFloat(u128, f64, args), + + // I128 conversion operations + .i128_to_i8_wrap => return self.intConvertWrap(i128, i8, args), + .i128_to_i8_try => return self.intConvertTry(i128, i8, args, return_rt_var), + .i128_to_i16_wrap => return self.intConvertWrap(i128, i16, args), + .i128_to_i16_try => return self.intConvertTry(i128, i16, args, return_rt_var), + .i128_to_i32_wrap => return self.intConvertWrap(i128, i32, args), + .i128_to_i32_try => return self.intConvertTry(i128, i32, args, return_rt_var), + .i128_to_i64_wrap => return self.intConvertWrap(i128, i64, args), + .i128_to_i64_try => return self.intConvertTry(i128, i64, args, return_rt_var), + .i128_to_u8_wrap => return self.intConvertWrap(i128, u8, args), + .i128_to_u8_try => return self.intConvertTry(i128, u8, args, return_rt_var), + .i128_to_u16_wrap => return self.intConvertWrap(i128, u16, args), + .i128_to_u16_try => return self.intConvertTry(i128, u16, args, return_rt_var), + .i128_to_u32_wrap => return self.intConvertWrap(i128, u32, args), + .i128_to_u32_try => return self.intConvertTry(i128, u32, args, return_rt_var), + .i128_to_u64_wrap => return self.intConvertWrap(i128, u64, args), + .i128_to_u64_try => return self.intConvertTry(i128, u64, args, return_rt_var), + .i128_to_u128_wrap => return self.intConvertWrap(i128, u128, args), + .i128_to_u128_try => return self.intConvertTry(i128, u128, args, return_rt_var), + .i128_to_f32 => return self.intToFloat(i128, f32, args), + .i128_to_f64 => return self.intToFloat(i128, f64, args), + + // U128 to Dec (try_unsafe - can overflow Dec's range) + .u128_to_dec_try_unsafe => return self.intToDecTryUnsafe(u128, args), + // I128 to Dec (try_unsafe - can overflow Dec's range) + .i128_to_dec_try_unsafe => return self.intToDecTryUnsafe(i128, args), + + // F32 conversion operations + .f32_to_i8_trunc => return self.floatToIntTrunc(f32, i8, args), + .f32_to_i8_try_unsafe => return self.floatToIntTryUnsafe(f32, i8, args), + .f32_to_i16_trunc => return self.floatToIntTrunc(f32, i16, args), + .f32_to_i16_try_unsafe => return self.floatToIntTryUnsafe(f32, i16, args), + .f32_to_i32_trunc => return self.floatToIntTrunc(f32, i32, args), + .f32_to_i32_try_unsafe => return self.floatToIntTryUnsafe(f32, i32, args), + .f32_to_i64_trunc => return self.floatToIntTrunc(f32, i64, args), + .f32_to_i64_try_unsafe => return self.floatToIntTryUnsafe(f32, i64, args), + .f32_to_i128_trunc => return self.floatToIntTrunc(f32, i128, args), + .f32_to_i128_try_unsafe => return self.floatToIntTryUnsafe(f32, i128, args), + .f32_to_u8_trunc => return self.floatToIntTrunc(f32, u8, args), + .f32_to_u8_try_unsafe => return self.floatToIntTryUnsafe(f32, u8, args), + .f32_to_u16_trunc => return self.floatToIntTrunc(f32, u16, args), + .f32_to_u16_try_unsafe => return self.floatToIntTryUnsafe(f32, u16, args), + .f32_to_u32_trunc => return self.floatToIntTrunc(f32, u32, args), + .f32_to_u32_try_unsafe => return self.floatToIntTryUnsafe(f32, u32, args), + .f32_to_u64_trunc => return self.floatToIntTrunc(f32, u64, args), + .f32_to_u64_try_unsafe => return self.floatToIntTryUnsafe(f32, u64, args), + .f32_to_u128_trunc => return self.floatToIntTrunc(f32, u128, args), + .f32_to_u128_try_unsafe => return self.floatToIntTryUnsafe(f32, u128, args), + .f32_to_f64 => return self.floatWiden(f32, f64, args), + + // F64 conversion operations + .f64_to_i8_trunc => return self.floatToIntTrunc(f64, i8, args), + .f64_to_i8_try_unsafe => return self.floatToIntTryUnsafe(f64, i8, args), + .f64_to_i16_trunc => return self.floatToIntTrunc(f64, i16, args), + .f64_to_i16_try_unsafe => return self.floatToIntTryUnsafe(f64, i16, args), + .f64_to_i32_trunc => return self.floatToIntTrunc(f64, i32, args), + .f64_to_i32_try_unsafe => return self.floatToIntTryUnsafe(f64, i32, args), + .f64_to_i64_trunc => return self.floatToIntTrunc(f64, i64, args), + .f64_to_i64_try_unsafe => return self.floatToIntTryUnsafe(f64, i64, args), + .f64_to_i128_trunc => return self.floatToIntTrunc(f64, i128, args), + .f64_to_i128_try_unsafe => return self.floatToIntTryUnsafe(f64, i128, args), + .f64_to_u8_trunc => return self.floatToIntTrunc(f64, u8, args), + .f64_to_u8_try_unsafe => return self.floatToIntTryUnsafe(f64, u8, args), + .f64_to_u16_trunc => return self.floatToIntTrunc(f64, u16, args), + .f64_to_u16_try_unsafe => return self.floatToIntTryUnsafe(f64, u16, args), + .f64_to_u32_trunc => return self.floatToIntTrunc(f64, u32, args), + .f64_to_u32_try_unsafe => return self.floatToIntTryUnsafe(f64, u32, args), + .f64_to_u64_trunc => return self.floatToIntTrunc(f64, u64, args), + .f64_to_u64_try_unsafe => return self.floatToIntTryUnsafe(f64, u64, args), + .f64_to_u128_trunc => return self.floatToIntTrunc(f64, u128, args), + .f64_to_u128_try_unsafe => return self.floatToIntTryUnsafe(f64, u128, args), + .f64_to_f32_wrap => return self.floatNarrow(f64, f32, args), + .f64_to_f32_try_unsafe => return self.floatNarrowTryUnsafe(f64, f32, args), + + // Dec conversion operations + .dec_to_i8_trunc => return self.decToIntTrunc(i8, args), + .dec_to_i8_try_unsafe => return self.decToIntTryUnsafe(i8, args), + .dec_to_i16_trunc => return self.decToIntTrunc(i16, args), + .dec_to_i16_try_unsafe => return self.decToIntTryUnsafe(i16, args), + .dec_to_i32_trunc => return self.decToIntTrunc(i32, args), + .dec_to_i32_try_unsafe => return self.decToIntTryUnsafe(i32, args), + .dec_to_i64_trunc => return self.decToIntTrunc(i64, args), + .dec_to_i64_try_unsafe => return self.decToIntTryUnsafe(i64, args), + .dec_to_i128_trunc => return self.decToIntTrunc(i128, args), + .dec_to_i128_try_unsafe => return self.decToI128TryUnsafe(args), + .dec_to_u8_trunc => return self.decToIntTrunc(u8, args), + .dec_to_u8_try_unsafe => return self.decToIntTryUnsafe(u8, args), + .dec_to_u16_trunc => return self.decToIntTrunc(u16, args), + .dec_to_u16_try_unsafe => return self.decToIntTryUnsafe(u16, args), + .dec_to_u32_trunc => return self.decToIntTrunc(u32, args), + .dec_to_u32_try_unsafe => return self.decToIntTryUnsafe(u32, args), + .dec_to_u64_trunc => return self.decToIntTrunc(u64, args), + .dec_to_u64_try_unsafe => return self.decToIntTryUnsafe(u64, args), + .dec_to_u128_trunc => return self.decToIntTrunc(u128, args), + .dec_to_u128_try_unsafe => return self.decToIntTryUnsafe(u128, args), + .dec_to_f32_wrap => return self.decToF32Wrap(args), + .dec_to_f32_try_unsafe => return self.decToF32TryUnsafe(args), + .dec_to_f64 => return self.decToF64(args), } } @@ -2778,6 +3036,353 @@ pub const Interpreter = struct { return out; } + /// Helper for integer to Dec try_unsafe conversions (for u128/i128 which can overflow) + /// Returns { success: Bool, val_or_memory_garbage: Dec } + fn intToDecTryUnsafe(self: *Interpreter, comptime From: type, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const int_arg = args[0]; + std.debug.assert(int_arg.ptr != null); + + const from_value: From = @as(*const From, @ptrCast(@alignCast(int_arg.ptr.?))).*; + + // Dec's max whole number is ~1.7×10^20, which is less than u128's max (~3.4×10^38) + // Dec is stored as i128 * 10^18, so max safe value is i128.max / 10^18 + const dec_max_whole: i128 = @divFloor(std.math.maxInt(i128), RocDec.one_point_zero_i128); + const dec_min_whole: i128 = @divFloor(std.math.minInt(i128), RocDec.one_point_zero_i128); + + // Check if conversion is safe + const success = if (From == u128) + from_value <= @as(u128, @intCast(dec_max_whole)) + else if (From == i128) + from_value >= dec_min_whole and from_value <= dec_max_whole + else + @compileError("intToDecTryUnsafe only supports u128 and i128"); + + // Build the result record: { success: Bool, val_or_memory_garbage: Dec } + return try self.buildSuccessValRecord(success, if (success) RocDec{ .num = @as(i128, @intCast(from_value)) * RocDec.one_point_zero_i128 } else RocDec{ .num = 0 }); + } + + /// Helper for float to int truncating conversions + fn floatToIntTrunc(self: *Interpreter, comptime From: type, comptime To: type, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const float_arg = args[0]; + std.debug.assert(float_arg.ptr != null); + + const from_value: From = @as(*const From, @ptrCast(@alignCast(float_arg.ptr.?))).*; + + // Truncate float to integer (clamping to range and truncating fractional part) + const to_value: To = floatToIntSaturating(From, To, from_value); + + const to_layout = Layout.int(comptime intTypeFromZigType(To)); + var out = try self.pushRaw(to_layout, 0); + out.is_initialized = false; + @as(*To, @ptrCast(@alignCast(out.ptr.?))).* = to_value; + out.is_initialized = true; + return out; + } + + /// Helper for float to int try_unsafe conversions + /// Returns { is_int: Bool, in_range: Bool, val_or_memory_garbage: To } + fn floatToIntTryUnsafe(self: *Interpreter, comptime From: type, comptime To: type, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const float_arg = args[0]; + std.debug.assert(float_arg.ptr != null); + + const from_value: From = @as(*const From, @ptrCast(@alignCast(float_arg.ptr.?))).*; + + // Check if it's an integer (no fractional part) and not NaN/Inf + const is_int = !std.math.isNan(from_value) and !std.math.isInf(from_value) and @trunc(from_value) == from_value; + + // Check if in range for target type + const min_val: From = @floatFromInt(std.math.minInt(To)); + const max_val: From = @floatFromInt(std.math.maxInt(To)); + const in_range = from_value >= min_val and from_value <= max_val; + + const val: To = if (is_int and in_range) @intFromFloat(from_value) else 0; + + // Build the result record: { is_int: Bool, in_range: Bool, val_or_memory_garbage: To } + return try self.buildIsIntInRangeValRecord(is_int, in_range, To, val); + } + + /// Helper for float widening (F32 -> F64) + fn floatWiden(self: *Interpreter, comptime From: type, comptime To: type, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const float_arg = args[0]; + std.debug.assert(float_arg.ptr != null); + + const from_value: From = @as(*const From, @ptrCast(@alignCast(float_arg.ptr.?))).*; + const to_value: To = @floatCast(from_value); + + const to_layout = Layout.frac(comptime fracTypeFromZigType(To)); + var out = try self.pushRaw(to_layout, 0); + out.is_initialized = false; + @as(*To, @ptrCast(@alignCast(out.ptr.?))).* = to_value; + out.is_initialized = true; + return out; + } + + /// Helper for float narrowing (F64 -> F32) + fn floatNarrow(self: *Interpreter, comptime From: type, comptime To: type, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const float_arg = args[0]; + std.debug.assert(float_arg.ptr != null); + + const from_value: From = @as(*const From, @ptrCast(@alignCast(float_arg.ptr.?))).*; + const to_value: To = @floatCast(from_value); + + const to_layout = Layout.frac(comptime fracTypeFromZigType(To)); + var out = try self.pushRaw(to_layout, 0); + out.is_initialized = false; + @as(*To, @ptrCast(@alignCast(out.ptr.?))).* = to_value; + out.is_initialized = true; + return out; + } + + /// Helper for float narrowing try_unsafe (F64 -> F32) + /// Returns { success: Bool, val_or_memory_garbage: F32 } + fn floatNarrowTryUnsafe(self: *Interpreter, comptime From: type, comptime To: type, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const float_arg = args[0]; + std.debug.assert(float_arg.ptr != null); + + const from_value: From = @as(*const From, @ptrCast(@alignCast(float_arg.ptr.?))).*; + const to_value: To = @floatCast(from_value); + + // Check if the conversion is lossless (converting back gives the same value) + // Also check for infinity which indicates overflow + const success = !std.math.isInf(to_value) or std.math.isInf(from_value); + const back: From = @floatCast(to_value); + const lossless = from_value == back or (std.math.isNan(from_value) and std.math.isNan(back)); + + return try self.buildSuccessValRecordF32(success and lossless, to_value); + } + + /// Helper for Dec to int truncating conversions + fn decToIntTrunc(self: *Interpreter, comptime To: type, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const dec_arg = args[0]; + std.debug.assert(dec_arg.ptr != null); + + const dec_value: RocDec = @as(*const RocDec, @ptrCast(@alignCast(dec_arg.ptr.?))).*; + + // Get the whole number part by dividing by one_point_zero + const whole_part = @divTrunc(dec_value.num, RocDec.one_point_zero_i128); + + // Saturate to target range + const to_value: To = std.math.cast(To, whole_part) orelse if (whole_part < 0) std.math.minInt(To) else std.math.maxInt(To); + + const to_layout = Layout.int(comptime intTypeFromZigType(To)); + var out = try self.pushRaw(to_layout, 0); + out.is_initialized = false; + @as(*To, @ptrCast(@alignCast(out.ptr.?))).* = to_value; + out.is_initialized = true; + return out; + } + + /// Helper for Dec to int try_unsafe conversions + /// Returns { is_int: Bool, in_range: Bool, val_or_memory_garbage: To } + fn decToIntTryUnsafe(self: *Interpreter, comptime To: type, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const dec_arg = args[0]; + std.debug.assert(dec_arg.ptr != null); + + const dec_value: RocDec = @as(*const RocDec, @ptrCast(@alignCast(dec_arg.ptr.?))).*; + + // Check if it's an integer (no fractional part) + const remainder = @rem(dec_value.num, RocDec.one_point_zero_i128); + const is_int = remainder == 0; + + // Get the whole number part + const whole_part = @divTrunc(dec_value.num, RocDec.one_point_zero_i128); + + // Check if in range for target type + const in_range = std.math.cast(To, whole_part) != null; + + const val: To = if (is_int and in_range) @intCast(whole_part) else 0; + + return try self.buildIsIntInRangeValRecord(is_int, in_range, To, val); + } + + /// Helper for Dec to i128 try_unsafe conversions (special case - always in range) + /// Returns { is_int: Bool, val_or_memory_garbage: I128 } + fn decToI128TryUnsafe(self: *Interpreter, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const dec_arg = args[0]; + std.debug.assert(dec_arg.ptr != null); + + const dec_value: RocDec = @as(*const RocDec, @ptrCast(@alignCast(dec_arg.ptr.?))).*; + + // Check if it's an integer (no fractional part) + const remainder = @rem(dec_value.num, RocDec.one_point_zero_i128); + const is_int = remainder == 0; + + // Get the whole number part - always fits in i128 + const whole_part = @divTrunc(dec_value.num, RocDec.one_point_zero_i128); + + return try self.buildIsIntValRecord(is_int, whole_part); + } + + /// Helper for Dec to F32 wrapping conversion + fn decToF32Wrap(self: *Interpreter, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const dec_arg = args[0]; + std.debug.assert(dec_arg.ptr != null); + + const dec_value: RocDec = @as(*const RocDec, @ptrCast(@alignCast(dec_arg.ptr.?))).*; + const f64_value = dec_value.toF64(); + const f32_value: f32 = @floatCast(f64_value); + + const to_layout = Layout.frac(.f32); + var out = try self.pushRaw(to_layout, 0); + out.is_initialized = false; + @as(*f32, @ptrCast(@alignCast(out.ptr.?))).* = f32_value; + out.is_initialized = true; + return out; + } + + /// Helper for Dec to F32 try_unsafe conversion + /// Returns { success: Bool, val_or_memory_garbage: F32 } + fn decToF32TryUnsafe(self: *Interpreter, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const dec_arg = args[0]; + std.debug.assert(dec_arg.ptr != null); + + const dec_value: RocDec = @as(*const RocDec, @ptrCast(@alignCast(dec_arg.ptr.?))).*; + const f64_value = dec_value.toF64(); + const f32_value: f32 = @floatCast(f64_value); + + // Check if conversion is lossless by converting back + const back_f64: f64 = @floatCast(f32_value); + const back_dec = RocDec.fromF64(back_f64); + const success = back_dec != null and back_dec.?.num == dec_value.num; + + return try self.buildSuccessValRecordF32(success, f32_value); + } + + /// Helper for Dec to F64 conversion + fn decToF64(self: *Interpreter, args: []const StackValue) !StackValue { + std.debug.assert(args.len == 1); + const dec_arg = args[0]; + std.debug.assert(dec_arg.ptr != null); + + const dec_value: RocDec = @as(*const RocDec, @ptrCast(@alignCast(dec_arg.ptr.?))).*; + const f64_value = dec_value.toF64(); + + const to_layout = Layout.frac(.f64); + var out = try self.pushRaw(to_layout, 0); + out.is_initialized = false; + @as(*f64, @ptrCast(@alignCast(out.ptr.?))).* = f64_value; + out.is_initialized = true; + return out; + } + + /// Build a record { success: Bool, val_or_memory_garbage: Dec } + fn buildSuccessValRecord(self: *Interpreter, success: bool, val: RocDec) !StackValue { + // Layout: tuple (Dec, Bool) where element 0 is Dec (16 bytes) and element 1 is Bool (1 byte) + // Total size with alignment: 24 bytes (16 for Dec + 8 for alignment of Bool field) + const dec_layout = Layout.frac(.dec); + const bool_layout = Layout.int(.u8); + + // We need to create a tuple layout for the result + // For now, allocate raw bytes and set them directly + // The tuple is (val_or_memory_garbage: Dec, success: Bool) + const tuple_size: usize = 24; // 16 bytes Dec + padding + 1 byte bool + var out = try self.pushRawBytes(tuple_size, 16); + out.is_initialized = false; + + // Write Dec at offset 0 + @as(*RocDec, @ptrCast(@alignCast(out.ptr.?))).* = val; + + // Write Bool at offset 16 + const bool_ptr: *u8 = @ptrFromInt(@intFromPtr(out.ptr.?) + 16); + bool_ptr.* = @intFromBool(success); + + out.is_initialized = true; + // Layout is set by pushRawBytes as .zst since we're working with raw bytes + _ = dec_layout; + _ = bool_layout; + return out; + } + + /// Build a record { success: Bool, val_or_memory_garbage: F32 } + fn buildSuccessValRecordF32(self: *Interpreter, success: bool, val: f32) !StackValue { + // Layout: tuple (F32, Bool) where element 0 is F32 (4 bytes) and element 1 is Bool (1 byte) + const tuple_size: usize = 8; // 4 bytes F32 + padding + 1 byte bool + var out = try self.pushRawBytes(tuple_size, 4); + out.is_initialized = false; + + // Write F32 at offset 0 + @as(*f32, @ptrCast(@alignCast(out.ptr.?))).* = val; + + // Write Bool at offset 4 + const bool_ptr: *u8 = @ptrFromInt(@intFromPtr(out.ptr.?) + 4); + bool_ptr.* = @intFromBool(success); + + out.is_initialized = true; + // Layout is set by pushRawBytes as .zst since we're working with raw bytes + return out; + } + + /// Build a record { is_int: Bool, in_range: Bool, val_or_memory_garbage: To } + fn buildIsIntInRangeValRecord(self: *Interpreter, is_int: bool, in_range: bool, comptime To: type, val: To) !StackValue { + // Layout depends on To's size + const val_size = @sizeOf(To); + const val_align = @alignOf(To); + // Structure: (val, is_int, in_range) with proper alignment + const tuple_size: usize = val_size + 2; // val + 2 bools + const padded_size = (tuple_size + val_align - 1) / val_align * val_align; + + var out = try self.pushRawBytes(padded_size, val_align); + out.is_initialized = false; + + // Write val at offset 0 + @as(*To, @ptrCast(@alignCast(out.ptr.?))).* = val; + + // Write is_int at offset val_size + const is_int_ptr: *u8 = @ptrFromInt(@intFromPtr(out.ptr.?) + val_size); + is_int_ptr.* = @intFromBool(is_int); + + // Write in_range at offset val_size + 1 + const in_range_ptr: *u8 = @ptrFromInt(@intFromPtr(out.ptr.?) + val_size + 1); + in_range_ptr.* = @intFromBool(in_range); + + out.is_initialized = true; + // Layout is set by pushRawBytes as .zst since we're working with raw bytes + return out; + } + + /// Build a record { is_int: Bool, val_or_memory_garbage: I128 } (for dec_to_i128 which is always in range) + fn buildIsIntValRecord(self: *Interpreter, is_int: bool, val: i128) !StackValue { + // Layout: tuple (I128, Bool) + const tuple_size: usize = 24; // 16 bytes I128 + padding + 1 byte bool + var out = try self.pushRawBytes(tuple_size, 16); + out.is_initialized = false; + + // Write I128 at offset 0 + @as(*i128, @ptrCast(@alignCast(out.ptr.?))).* = val; + + // Write Bool at offset 16 + const bool_ptr: *u8 = @ptrFromInt(@intFromPtr(out.ptr.?) + 16); + bool_ptr.* = @intFromBool(is_int); + + out.is_initialized = true; + // Layout is set by pushRawBytes as .zst since we're working with raw bytes + return out; + } + + /// Helper to convert float to int with saturation (for trunc operations) + fn floatToIntSaturating(comptime From: type, comptime To: type, value: From) To { + if (std.math.isNan(value)) return 0; + + const min_val: From = @floatFromInt(std.math.minInt(To)); + const max_val: From = @floatFromInt(std.math.maxInt(To)); + + if (value <= min_val) return std.math.minInt(To); + if (value >= max_val) return std.math.maxInt(To); + + return @intFromFloat(value); + } + /// Convert Zig integer type to types.Int.Precision fn intTypeFromZigType(comptime T: type) types.Int.Precision { return switch (T) {