mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-03 16:44:33 +00:00
Merge pull request #2472 from rtfeldman/add_Int.toInt_builtins
Add `{Int *}.to{Int *}` builtins
This commit is contained in:
commit
74daec84df
15 changed files with 929 additions and 21 deletions
|
@ -28,7 +28,7 @@ There will be two directories like `roc_builtins-[some random characters]`, look
|
||||||
|
|
||||||
> The bitcode is a bunch of bytes that aren't particularly human-readable.
|
> The bitcode is a bunch of bytes that aren't particularly human-readable.
|
||||||
> If you want to take a look at the human-readable LLVM IR, look at
|
> If you want to take a look at the human-readable LLVM IR, look at
|
||||||
> `target/debug/build/roc_builtins-[some random characters]/out/builtins.ll`
|
> `compiler/builtins/bitcode/builtins.ll`
|
||||||
|
|
||||||
## Calling bitcode functions
|
## Calling bitcode functions
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,14 @@ comptime {
|
||||||
num.exportDivCeil(T, ROC_BUILTINS ++ "." ++ NUM ++ ".div_ceil.");
|
num.exportDivCeil(T, ROC_BUILTINS ++ "." ++ NUM ++ ".div_ceil.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline for (INTEGERS) |FROM| {
|
||||||
|
inline for (INTEGERS) |TO| {
|
||||||
|
// We're exporting more than we need here, but that's okay.
|
||||||
|
num.exportToIntCheckingMax(FROM, TO, ROC_BUILTINS ++ "." ++ NUM ++ ".int_to_" ++ @typeName(TO) ++ "_checking_max.");
|
||||||
|
num.exportToIntCheckingMaxAndMin(FROM, TO, ROC_BUILTINS ++ "." ++ NUM ++ ".int_to_" ++ @typeName(TO) ++ "_checking_max_and_min.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
inline for (FLOATS) |T| {
|
inline for (FLOATS) |T| {
|
||||||
num.exportAsin(T, ROC_BUILTINS ++ "." ++ NUM ++ ".asin.");
|
num.exportAsin(T, ROC_BUILTINS ++ "." ++ NUM ++ ".asin.");
|
||||||
num.exportAcos(T, ROC_BUILTINS ++ "." ++ NUM ++ ".acos.");
|
num.exportAcos(T, ROC_BUILTINS ++ "." ++ NUM ++ ".acos.");
|
||||||
|
|
|
@ -108,6 +108,39 @@ pub fn exportDivCeil(comptime T: type, comptime name: []const u8) void {
|
||||||
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
@export(f, .{ .name = name ++ @typeName(T), .linkage = .Strong });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ToIntCheckedResult(comptime T: type) type {
|
||||||
|
// On the Roc side we sort by alignment; putting the errorcode last
|
||||||
|
// always works out (no number with smaller alignment than 1).
|
||||||
|
return extern struct {
|
||||||
|
value: T,
|
||||||
|
out_of_bounds: bool,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exportToIntCheckingMax(comptime From: type, comptime To: type, comptime name: []const u8) void {
|
||||||
|
comptime var f = struct {
|
||||||
|
fn func(input: From) callconv(.C) ToIntCheckedResult(To) {
|
||||||
|
if (input > std.math.maxInt(To)) {
|
||||||
|
return .{ .out_of_bounds = true, .value = 0 };
|
||||||
|
}
|
||||||
|
return .{ .out_of_bounds = false, .value = @intCast(To, input) };
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(From), .linkage = .Strong });
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exportToIntCheckingMaxAndMin(comptime From: type, comptime To: type, comptime name: []const u8) void {
|
||||||
|
comptime var f = struct {
|
||||||
|
fn func(input: From) callconv(.C) ToIntCheckedResult(To) {
|
||||||
|
if (input > std.math.maxInt(To) or input < std.math.minInt(To)) {
|
||||||
|
return .{ .out_of_bounds = true, .value = 0 };
|
||||||
|
}
|
||||||
|
return .{ .out_of_bounds = false, .value = @intCast(To, input) };
|
||||||
|
}
|
||||||
|
}.func;
|
||||||
|
@export(f, .{ .name = name ++ @typeName(From), .linkage = .Strong });
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bytesToU16C(arg: RocList, position: usize) callconv(.C) u16 {
|
pub fn bytesToU16C(arg: RocList, position: usize) callconv(.C) u16 {
|
||||||
return @call(.{ .modifier = always_inline }, bytesToU16, .{ arg, position });
|
return @call(.{ .modifier = always_inline }, bytesToU16, .{ arg, position });
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,6 +100,26 @@ interface Num
|
||||||
subWrap,
|
subWrap,
|
||||||
sqrt,
|
sqrt,
|
||||||
tan,
|
tan,
|
||||||
|
toI8,
|
||||||
|
toI8Checked,
|
||||||
|
toI16,
|
||||||
|
toI16Checked,
|
||||||
|
toI32,
|
||||||
|
toI32Checked,
|
||||||
|
toI64,
|
||||||
|
toI64Checked,
|
||||||
|
toI128,
|
||||||
|
toI128Checked,
|
||||||
|
toU8,
|
||||||
|
toU8Checked,
|
||||||
|
toU16,
|
||||||
|
toU16Checked,
|
||||||
|
toU32,
|
||||||
|
toU32Checked,
|
||||||
|
toU64,
|
||||||
|
toU64Checked,
|
||||||
|
toU128,
|
||||||
|
toU128Checked,
|
||||||
toFloat,
|
toFloat,
|
||||||
toStr
|
toStr
|
||||||
]
|
]
|
||||||
|
@ -592,6 +612,35 @@ mulCheckOverflow : Num a, Num a -> Result (Num a) [ Overflow ]*
|
||||||
|
|
||||||
## Convert
|
## Convert
|
||||||
|
|
||||||
|
## Convert any [Int] to a specifically-sized [Int], without checking validity.
|
||||||
|
## These are unchecked bitwise operations,
|
||||||
|
## so if the source number is outside the target range, then these will
|
||||||
|
## effectively modulo-wrap around the target range to reach a valid value.
|
||||||
|
toI8 : Int * -> I8
|
||||||
|
toI16 : Int * -> I16
|
||||||
|
toI32 : Int * -> I32
|
||||||
|
toI64 : Int * -> I64
|
||||||
|
toI128 : Int * -> I128
|
||||||
|
toU8 : Int * -> U8
|
||||||
|
toU16 : Int * -> U16
|
||||||
|
toU32 : Int * -> U32
|
||||||
|
toU64 : Int * -> U64
|
||||||
|
toU128 : Int * -> U128
|
||||||
|
## Convert any [Int] to a specifically-sized [Int], after checking validity.
|
||||||
|
## These are checked bitwise operations,
|
||||||
|
## so if the source number is outside the target range, then these will
|
||||||
|
## return `Err OutOfBounds`.
|
||||||
|
toI8Checked : Int * -> Result I8 [ OutOfBounds ]*
|
||||||
|
toI16Checked : Int * -> Result I16 [ OutOfBounds ]*
|
||||||
|
toI32Checked : Int * -> Result I32 [ OutOfBounds ]*
|
||||||
|
toI64Checked : Int * -> Result I64 [ OutOfBounds ]*
|
||||||
|
toI128Checked : Int * -> Result I128 [ OutOfBounds ]*
|
||||||
|
toU8Checked : Int * -> Result U8 [ OutOfBounds ]*
|
||||||
|
toU16Checked : Int * -> Result U16 [ OutOfBounds ]*
|
||||||
|
toU32Checked : Int * -> Result U32 [ OutOfBounds ]*
|
||||||
|
toU64Checked : Int * -> Result U64 [ OutOfBounds ]*
|
||||||
|
toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
|
||||||
|
|
||||||
## Convert a number to a [Str].
|
## Convert a number to a [Str].
|
||||||
##
|
##
|
||||||
## This is the same as calling `Num.format {}` - so for more details on
|
## This is the same as calling `Num.format {}` - so for more details on
|
||||||
|
|
|
@ -12,7 +12,7 @@ pub const BUILTINS_WASM32_OBJ_PATH: &str = env!(
|
||||||
"Env var BUILTINS_WASM32_O not found. Is there a problem with the build script?"
|
"Env var BUILTINS_WASM32_O not found. Is there a problem with the build script?"
|
||||||
);
|
);
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default, Copy, Clone)]
|
||||||
pub struct IntrinsicName {
|
pub struct IntrinsicName {
|
||||||
pub options: [&'static str; 14],
|
pub options: [&'static str; 14],
|
||||||
}
|
}
|
||||||
|
@ -159,6 +159,21 @@ impl IntWidth {
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub const fn type_name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::I8 => "i8",
|
||||||
|
Self::I16 => "i16",
|
||||||
|
Self::I32 => "i32",
|
||||||
|
Self::I64 => "i64",
|
||||||
|
Self::I128 => "i128",
|
||||||
|
Self::U8 => "u8",
|
||||||
|
Self::U16 => "u16",
|
||||||
|
Self::U32 => "u32",
|
||||||
|
Self::U64 => "u64",
|
||||||
|
Self::U128 => "u128",
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<DecWidth> for IntrinsicName {
|
impl Index<DecWidth> for IntrinsicName {
|
||||||
|
@ -214,11 +229,12 @@ macro_rules! float_intrinsic {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[macro_export]
|
#[macro_export]
|
||||||
macro_rules! int_intrinsic {
|
macro_rules! llvm_int_intrinsic {
|
||||||
($signed_name:literal, $unsigned_name:literal) => {{
|
($signed_name:literal, $unsigned_name:literal) => {{
|
||||||
let mut output = IntrinsicName::default();
|
let mut output = IntrinsicName::default();
|
||||||
|
|
||||||
// The indeces align with the `Index` impl for `IntrinsicName`.
|
// The indeces align with the `Index` impl for `IntrinsicName`.
|
||||||
|
// LLVM uses the same types for both signed and unsigned integers.
|
||||||
output.options[4] = concat!($unsigned_name, ".i8");
|
output.options[4] = concat!($unsigned_name, ".i8");
|
||||||
output.options[5] = concat!($unsigned_name, ".i16");
|
output.options[5] = concat!($unsigned_name, ".i16");
|
||||||
output.options[6] = concat!($unsigned_name, ".i32");
|
output.options[6] = concat!($unsigned_name, ".i32");
|
||||||
|
@ -239,6 +255,28 @@ macro_rules! int_intrinsic {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! int_intrinsic {
|
||||||
|
($name:expr) => {{
|
||||||
|
let mut output = IntrinsicName::default();
|
||||||
|
|
||||||
|
// The indices align with the `Index` impl for `IntrinsicName`.
|
||||||
|
output.options[4] = concat!($name, ".u8");
|
||||||
|
output.options[5] = concat!($name, ".u16");
|
||||||
|
output.options[6] = concat!($name, ".u32");
|
||||||
|
output.options[7] = concat!($name, ".u64");
|
||||||
|
output.options[8] = concat!($name, ".u128");
|
||||||
|
|
||||||
|
output.options[9] = concat!($name, ".i8");
|
||||||
|
output.options[10] = concat!($name, ".i16");
|
||||||
|
output.options[11] = concat!($name, ".i32");
|
||||||
|
output.options[12] = concat!($name, ".i64");
|
||||||
|
output.options[13] = concat!($name, ".i128");
|
||||||
|
|
||||||
|
output
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
pub const NUM_ASIN: IntrinsicName = float_intrinsic!("roc_builtins.num.asin");
|
pub const NUM_ASIN: IntrinsicName = float_intrinsic!("roc_builtins.num.asin");
|
||||||
pub const NUM_ACOS: IntrinsicName = float_intrinsic!("roc_builtins.num.acos");
|
pub const NUM_ACOS: IntrinsicName = float_intrinsic!("roc_builtins.num.acos");
|
||||||
pub const NUM_ATAN: IntrinsicName = float_intrinsic!("roc_builtins.num.atan");
|
pub const NUM_ATAN: IntrinsicName = float_intrinsic!("roc_builtins.num.atan");
|
||||||
|
@ -339,3 +377,50 @@ pub const UTILS_DECREF_CHECK_NULL: &str = "roc_builtins.utils.decref_check_null"
|
||||||
pub const UTILS_EXPECT_FAILED: &str = "roc_builtins.expect.expect_failed";
|
pub const UTILS_EXPECT_FAILED: &str = "roc_builtins.expect.expect_failed";
|
||||||
pub const UTILS_GET_EXPECT_FAILURES: &str = "roc_builtins.expect.get_expect_failures";
|
pub const UTILS_GET_EXPECT_FAILURES: &str = "roc_builtins.expect.get_expect_failures";
|
||||||
pub const UTILS_DEINIT_FAILURES: &str = "roc_builtins.expect.deinit_failures";
|
pub const UTILS_DEINIT_FAILURES: &str = "roc_builtins.expect.deinit_failures";
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct IntToIntrinsicName {
|
||||||
|
pub options: [IntrinsicName; 10],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IntToIntrinsicName {
|
||||||
|
pub const fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
options: [IntrinsicName::default(); 10],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Index<IntWidth> for IntToIntrinsicName {
|
||||||
|
type Output = IntrinsicName;
|
||||||
|
|
||||||
|
fn index(&self, index: IntWidth) -> &Self::Output {
|
||||||
|
&self.options[index as usize]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! int_to_int_intrinsic {
|
||||||
|
($name_prefix:literal, $name_suffix:literal) => {{
|
||||||
|
let mut output = IntToIntrinsicName::default();
|
||||||
|
|
||||||
|
output.options[0] = int_intrinsic!(concat!($name_prefix, "u8", $name_suffix));
|
||||||
|
output.options[1] = int_intrinsic!(concat!($name_prefix, "u16", $name_suffix));
|
||||||
|
output.options[2] = int_intrinsic!(concat!($name_prefix, "u32", $name_suffix));
|
||||||
|
output.options[3] = int_intrinsic!(concat!($name_prefix, "u64", $name_suffix));
|
||||||
|
output.options[4] = int_intrinsic!(concat!($name_prefix, "u128", $name_suffix));
|
||||||
|
|
||||||
|
output.options[5] = int_intrinsic!(concat!($name_prefix, "i8", $name_suffix));
|
||||||
|
output.options[6] = int_intrinsic!(concat!($name_prefix, "i16", $name_suffix));
|
||||||
|
output.options[7] = int_intrinsic!(concat!($name_prefix, "i32", $name_suffix));
|
||||||
|
output.options[8] = int_intrinsic!(concat!($name_prefix, "i64", $name_suffix));
|
||||||
|
output.options[9] = int_intrinsic!(concat!($name_prefix, "i128", $name_suffix));
|
||||||
|
|
||||||
|
output
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const NUM_INT_TO_INT_CHECKING_MAX: IntToIntrinsicName =
|
||||||
|
int_to_int_intrinsic!("roc_builtins.num.int_to_", "_checking_max");
|
||||||
|
pub const NUM_INT_TO_INT_CHECKING_MAX_AND_MIN: IntToIntrinsicName =
|
||||||
|
int_to_int_intrinsic!("roc_builtins.num.int_to_", "_checking_max_and_min");
|
||||||
|
|
|
@ -445,6 +445,156 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
|
||||||
// maxI128 : I128
|
// maxI128 : I128
|
||||||
add_type!(Symbol::NUM_MAX_I128, i128_type());
|
add_type!(Symbol::NUM_MAX_I128, i128_type());
|
||||||
|
|
||||||
|
// toI8 : Int * -> I8
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_I8,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(i8_type()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let out_of_bounds = SolvedType::TagUnion(
|
||||||
|
vec![(TagName::Global("OutOfBounds".into()), vec![])],
|
||||||
|
Box::new(SolvedType::Wildcard),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toI8Checked : Int * -> Result I8 [ OutOfBounds ]*
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_I8_CHECKED,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(i8_type(), out_of_bounds.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toI16 : Int * -> I16
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_I16,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(i16_type()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toI16Checked : Int * -> Result I16 [ OutOfBounds ]*
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_I16_CHECKED,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(i16_type(), out_of_bounds.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toI32 : Int * -> I32
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_I32,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(i32_type()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toI32Checked : Int * -> Result I32 [ OutOfBounds ]*
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_I32_CHECKED,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(i32_type(), out_of_bounds.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toI64 : Int * -> I64
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_I64,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(i64_type()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toI64Checked : Int * -> Result I64 [ OutOfBounds ]*
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_I64_CHECKED,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(i64_type(), out_of_bounds.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toI128 : Int * -> I128
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_I128,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(i128_type()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toI128Checked : Int * -> Result I128 [ OutOfBounds ]*
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_I128_CHECKED,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(i128_type(), out_of_bounds)),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toU8 : Int * -> U8
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_U8,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(u8_type()),
|
||||||
|
);
|
||||||
|
|
||||||
|
let out_of_bounds = SolvedType::TagUnion(
|
||||||
|
vec![(TagName::Global("OutOfBounds".into()), vec![])],
|
||||||
|
Box::new(SolvedType::Wildcard),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toU8Checked : Int * -> Result U8 [ OutOfBounds ]*
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_U8_CHECKED,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(u8_type(), out_of_bounds.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toU16 : Int * -> U16
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_U16,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(u16_type()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toU16Checked : Int * -> Result U16 [ OutOfBounds ]*
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_U16_CHECKED,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(u16_type(), out_of_bounds.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toU32 : Int * -> U32
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_U32,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(u32_type()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toU32Checked : Int * -> Result U32 [ OutOfBounds ]*
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_U32_CHECKED,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(u32_type(), out_of_bounds.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toU64 : Int * -> U64
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_U64,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(u64_type()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toU64Checked : Int * -> Result U64 [ OutOfBounds ]*
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_U64_CHECKED,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(u64_type(), out_of_bounds.clone())),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toU128 : Int * -> U128
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_U128,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(u128_type()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// toU128Checked : Int * -> Result U128 [ OutOfBounds ]*
|
||||||
|
add_top_level_function_type!(
|
||||||
|
Symbol::NUM_TO_U128_CHECKED,
|
||||||
|
vec![int_type(flex(TVAR1))],
|
||||||
|
Box::new(result_type(u128_type(), out_of_bounds)),
|
||||||
|
);
|
||||||
|
|
||||||
// toStr : Num a -> Str
|
// toStr : Num a -> Str
|
||||||
add_top_level_function_type!(
|
add_top_level_function_type!(
|
||||||
Symbol::NUM_TO_STR,
|
Symbol::NUM_TO_STR,
|
||||||
|
|
|
@ -242,6 +242,26 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
|
||||||
NUM_MAX_U64=> num_max_u64,
|
NUM_MAX_U64=> num_max_u64,
|
||||||
NUM_MIN_I128=> num_min_i128,
|
NUM_MIN_I128=> num_min_i128,
|
||||||
NUM_MAX_I128=> num_max_i128,
|
NUM_MAX_I128=> num_max_i128,
|
||||||
|
NUM_TO_I8 => num_to_i8,
|
||||||
|
NUM_TO_I8_CHECKED => num_to_i8_checked,
|
||||||
|
NUM_TO_I16 => num_to_i16,
|
||||||
|
NUM_TO_I16_CHECKED => num_to_i16_checked,
|
||||||
|
NUM_TO_I32 => num_to_i32,
|
||||||
|
NUM_TO_I32_CHECKED => num_to_i32_checked,
|
||||||
|
NUM_TO_I64 => num_to_i64,
|
||||||
|
NUM_TO_I64_CHECKED => num_to_i64_checked,
|
||||||
|
NUM_TO_I128 => num_to_i128,
|
||||||
|
NUM_TO_I128_CHECKED => num_to_i128_checked,
|
||||||
|
NUM_TO_U8 => num_to_u8,
|
||||||
|
NUM_TO_U8_CHECKED => num_to_u8_checked,
|
||||||
|
NUM_TO_U16 => num_to_u16,
|
||||||
|
NUM_TO_U16_CHECKED => num_to_u16_checked,
|
||||||
|
NUM_TO_U32 => num_to_u32,
|
||||||
|
NUM_TO_U32_CHECKED => num_to_u32_checked,
|
||||||
|
NUM_TO_U64 => num_to_u64,
|
||||||
|
NUM_TO_U64_CHECKED => num_to_u64_checked,
|
||||||
|
NUM_TO_U128 => num_to_u128,
|
||||||
|
NUM_TO_U128_CHECKED => num_to_u128_checked,
|
||||||
NUM_TO_STR => num_to_str,
|
NUM_TO_STR => num_to_str,
|
||||||
RESULT_MAP => result_map,
|
RESULT_MAP => result_map,
|
||||||
RESULT_MAP_ERR => result_map_err,
|
RESULT_MAP_ERR => result_map_err,
|
||||||
|
@ -390,6 +410,174 @@ fn lowlevel_5(symbol: Symbol, op: LowLevel, var_store: &mut VarStore) -> Def {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Num.toI8 : Int * -> I8
|
||||||
|
fn num_to_i8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Defer to IntCast
|
||||||
|
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Num.toI16 : Int * -> I16
|
||||||
|
fn num_to_i16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Defer to IntCast
|
||||||
|
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Num.toI32 : Int * -> I32
|
||||||
|
fn num_to_i32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Defer to IntCast
|
||||||
|
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Num.toI64 : Int * -> I64
|
||||||
|
fn num_to_i64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Defer to IntCast
|
||||||
|
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Num.toI128 : Int * -> I128
|
||||||
|
fn num_to_i128(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Defer to IntCast
|
||||||
|
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Num.toU8 : Int * -> U8
|
||||||
|
fn num_to_u8(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Defer to IntCast
|
||||||
|
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Num.toU16 : Int * -> U16
|
||||||
|
fn num_to_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Defer to IntCast
|
||||||
|
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Num.toU32 : Int * -> U32
|
||||||
|
fn num_to_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Defer to IntCast
|
||||||
|
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Num.toU64 : Int * -> U64
|
||||||
|
fn num_to_u64(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Defer to IntCast
|
||||||
|
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Num.toU128 : Int * -> U128
|
||||||
|
fn num_to_u128(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Defer to IntCast
|
||||||
|
lowlevel_1(symbol, LowLevel::NumIntCast, var_store)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_num_checked(symbol: Symbol, var_store: &mut VarStore, lowlevel: LowLevel) -> Def {
|
||||||
|
let bool_var = var_store.fresh();
|
||||||
|
let num_var_1 = var_store.fresh();
|
||||||
|
let num_var_2 = var_store.fresh();
|
||||||
|
let ret_var = var_store.fresh();
|
||||||
|
let record_var = var_store.fresh();
|
||||||
|
|
||||||
|
// let arg_2 = RunLowLevel NumToXXXChecked arg_1
|
||||||
|
// if arg_2.b then
|
||||||
|
// Err OutOfBounds
|
||||||
|
// else
|
||||||
|
// Ok arg_2.a
|
||||||
|
//
|
||||||
|
// "a" and "b" because the lowlevel return value looks like { converted_val: XXX, out_of_bounds: bool },
|
||||||
|
// and codegen will sort by alignment, so "a" will be the first key, etc.
|
||||||
|
|
||||||
|
let cont = If {
|
||||||
|
branch_var: ret_var,
|
||||||
|
cond_var: bool_var,
|
||||||
|
branches: vec![(
|
||||||
|
// if-condition
|
||||||
|
no_region(
|
||||||
|
// arg_2.b
|
||||||
|
Access {
|
||||||
|
record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "b".into(),
|
||||||
|
field_var: var_store.fresh(),
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_2))),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
// out of bounds!
|
||||||
|
no_region(tag(
|
||||||
|
"Err",
|
||||||
|
vec![tag("OutOfBounds", Vec::new(), var_store)],
|
||||||
|
var_store,
|
||||||
|
)),
|
||||||
|
)],
|
||||||
|
final_else: Box::new(
|
||||||
|
// all is well
|
||||||
|
no_region(
|
||||||
|
// Ok arg_2.a
|
||||||
|
tag(
|
||||||
|
"Ok",
|
||||||
|
vec![
|
||||||
|
// arg_2.a
|
||||||
|
Access {
|
||||||
|
record_var,
|
||||||
|
ext_var: var_store.fresh(),
|
||||||
|
field: "a".into(),
|
||||||
|
field_var: num_var_2,
|
||||||
|
loc_expr: Box::new(no_region(Var(Symbol::ARG_2))),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
var_store,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
// arg_2 = RunLowLevel NumToXXXChecked arg_1
|
||||||
|
let def = crate::def::Def {
|
||||||
|
loc_pattern: no_region(Pattern::Identifier(Symbol::ARG_2)),
|
||||||
|
loc_expr: no_region(RunLowLevel {
|
||||||
|
op: lowlevel,
|
||||||
|
args: vec![(num_var_1, Var(Symbol::ARG_1))],
|
||||||
|
ret_var: record_var,
|
||||||
|
}),
|
||||||
|
expr_var: record_var,
|
||||||
|
pattern_vars: SendMap::default(),
|
||||||
|
annotation: None,
|
||||||
|
};
|
||||||
|
|
||||||
|
let body = LetNonRec(Box::new(def), Box::new(no_region(cont)), ret_var);
|
||||||
|
|
||||||
|
defn(
|
||||||
|
symbol,
|
||||||
|
vec![(num_var_1, Symbol::ARG_1)],
|
||||||
|
var_store,
|
||||||
|
body,
|
||||||
|
ret_var,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! num_to_checked {
|
||||||
|
($($fn:ident)*) => {$(
|
||||||
|
// Num.toXXXChecked : Int * -> Result XXX [ OutOfBounds ]*
|
||||||
|
fn $fn(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
|
// Use the generic `NumToIntChecked`; we'll figure out exactly what layout(s) we need
|
||||||
|
// during code generation after types are resolved.
|
||||||
|
to_num_checked(symbol, var_store, LowLevel::NumToIntChecked)
|
||||||
|
}
|
||||||
|
)*}
|
||||||
|
}
|
||||||
|
|
||||||
|
num_to_checked! {
|
||||||
|
num_to_i8_checked
|
||||||
|
num_to_i16_checked
|
||||||
|
num_to_i32_checked
|
||||||
|
num_to_i64_checked
|
||||||
|
num_to_i128_checked
|
||||||
|
num_to_u8_checked
|
||||||
|
num_to_u16_checked
|
||||||
|
num_to_u32_checked
|
||||||
|
num_to_u64_checked
|
||||||
|
num_to_u128_checked
|
||||||
|
}
|
||||||
|
|
||||||
// Num.toStr : Num a -> Str
|
// Num.toStr : Num a -> Str
|
||||||
fn num_to_str(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
fn num_to_str(symbol: Symbol, var_store: &mut VarStore) -> Def {
|
||||||
let num_var = var_store.fresh();
|
let num_var = var_store.fresh();
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
/// Helpers for interacting with the zig that generates bitcode
|
/// Helpers for interacting with the zig that generates bitcode
|
||||||
use crate::debug_info_init;
|
use crate::debug_info_init;
|
||||||
use crate::llvm::build::{
|
use crate::llvm::build::{
|
||||||
load_roc_value, struct_from_fields, Env, C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX,
|
complex_bitcast_check_size, load_roc_value, struct_from_fields, to_cc_return, CCReturn, Env,
|
||||||
|
C_CALL_CONV, FAST_CALL_CONV, TAG_DATA_INDEX,
|
||||||
};
|
};
|
||||||
use crate::llvm::convert::basic_type_from_layout;
|
use crate::llvm::convert::basic_type_from_layout;
|
||||||
use crate::llvm::refcounting::{
|
use crate::llvm::refcounting::{
|
||||||
|
@ -11,9 +12,12 @@ use inkwell::attributes::{Attribute, AttributeLoc};
|
||||||
use inkwell::types::{BasicType, BasicTypeEnum};
|
use inkwell::types::{BasicType, BasicTypeEnum};
|
||||||
use inkwell::values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
|
use inkwell::values::{BasicValue, BasicValueEnum, CallSiteValue, FunctionValue, InstructionValue};
|
||||||
use inkwell::AddressSpace;
|
use inkwell::AddressSpace;
|
||||||
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::symbol::Symbol;
|
use roc_module::symbol::Symbol;
|
||||||
use roc_mono::layout::{LambdaSet, Layout, LayoutIds, UnionLayout};
|
use roc_mono::layout::{LambdaSet, Layout, LayoutIds, UnionLayout};
|
||||||
|
|
||||||
|
use std::convert::TryInto;
|
||||||
|
|
||||||
pub fn call_bitcode_fn<'a, 'ctx, 'env>(
|
pub fn call_bitcode_fn<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
args: &[BasicValueEnum<'ctx>],
|
args: &[BasicValueEnum<'ctx>],
|
||||||
|
@ -92,6 +96,63 @@ fn call_bitcode_fn_help<'a, 'ctx, 'env>(
|
||||||
call
|
call
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn call_bitcode_fn_fixing_for_convention<'a, 'ctx, 'env>(
|
||||||
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
|
args: &[BasicValueEnum<'ctx>],
|
||||||
|
return_layout: &Layout<'_>,
|
||||||
|
fn_name: &str,
|
||||||
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
// Calling zig bitcode, so we must follow C calling conventions.
|
||||||
|
let cc_return = to_cc_return(env, return_layout);
|
||||||
|
match cc_return {
|
||||||
|
CCReturn::Return => {
|
||||||
|
// We'll get a return value
|
||||||
|
call_bitcode_fn(env, args, fn_name)
|
||||||
|
}
|
||||||
|
CCReturn::ByPointer => {
|
||||||
|
// We need to pass the return value by pointer.
|
||||||
|
let roc_return_type = basic_type_from_layout(env, return_layout);
|
||||||
|
|
||||||
|
let cc_ptr_return_type = env
|
||||||
|
.module
|
||||||
|
.get_function(fn_name)
|
||||||
|
.unwrap()
|
||||||
|
.get_type()
|
||||||
|
.get_param_types()[0]
|
||||||
|
.into_pointer_type();
|
||||||
|
let cc_return_type: BasicTypeEnum<'ctx> = cc_ptr_return_type
|
||||||
|
.get_element_type()
|
||||||
|
.try_into()
|
||||||
|
.expect("Zig bitcode return type is not a basic type!");
|
||||||
|
|
||||||
|
let cc_return_value_ptr = env.builder.build_alloca(cc_return_type, "return_value");
|
||||||
|
let fixed_args: Vec<BasicValueEnum<'ctx>> = [cc_return_value_ptr.into()]
|
||||||
|
.iter()
|
||||||
|
.chain(args)
|
||||||
|
.copied()
|
||||||
|
.collect();
|
||||||
|
call_void_bitcode_fn(env, &fixed_args, fn_name);
|
||||||
|
|
||||||
|
let cc_return_value = env.builder.build_load(cc_return_value_ptr, "read_result");
|
||||||
|
if roc_return_type.size_of() == cc_return_type.size_of() {
|
||||||
|
cc_return_value
|
||||||
|
} else {
|
||||||
|
// We need to convert the C-callconv return type, which may be larger than the Roc
|
||||||
|
// return type, into the Roc return type.
|
||||||
|
complex_bitcast_check_size(
|
||||||
|
env,
|
||||||
|
cc_return_value,
|
||||||
|
roc_return_type,
|
||||||
|
"c_value_to_roc_value",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CCReturn::Void => {
|
||||||
|
internal_error!("Tried to call valued bitcode function, but it has no return type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const ARGUMENT_SYMBOLS: [Symbol; 8] = [
|
const ARGUMENT_SYMBOLS: [Symbol; 8] = [
|
||||||
Symbol::ARG_1,
|
Symbol::ARG_1,
|
||||||
Symbol::ARG_2,
|
Symbol::ARG_2,
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
use std::convert::TryFrom;
|
use std::convert::TryFrom;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
use crate::llvm::bitcode::{call_bitcode_fn, call_void_bitcode_fn};
|
use crate::llvm::bitcode::{
|
||||||
|
call_bitcode_fn, call_bitcode_fn_fixing_for_convention, call_void_bitcode_fn,
|
||||||
|
};
|
||||||
use crate::llvm::build_dict::{
|
use crate::llvm::build_dict::{
|
||||||
self, dict_contains, dict_difference, dict_empty, dict_get, dict_insert, dict_intersection,
|
self, dict_contains, dict_difference, dict_empty, dict_get, dict_insert, dict_intersection,
|
||||||
dict_keys, dict_len, dict_remove, dict_union, dict_values, dict_walk, set_from_list,
|
dict_keys, dict_len, dict_remove, dict_union, dict_values, dict_walk, set_from_list,
|
||||||
|
@ -53,7 +55,7 @@ use morphic_lib::{
|
||||||
CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions, UpdateMode, UpdateModeVar,
|
CalleeSpecVar, FuncName, FuncSpec, FuncSpecSolutions, ModSolutions, UpdateMode, UpdateModeVar,
|
||||||
};
|
};
|
||||||
use roc_builtins::bitcode::{self, FloatWidth, IntWidth, IntrinsicName};
|
use roc_builtins::bitcode::{self, FloatWidth, IntWidth, IntrinsicName};
|
||||||
use roc_builtins::{float_intrinsic, int_intrinsic};
|
use roc_builtins::{float_intrinsic, llvm_int_intrinsic};
|
||||||
use roc_collections::all::{ImMap, MutMap, MutSet};
|
use roc_collections::all::{ImMap, MutMap, MutSet};
|
||||||
use roc_error_macros::internal_error;
|
use roc_error_macros::internal_error;
|
||||||
use roc_module::low_level::LowLevel;
|
use roc_module::low_level::LowLevel;
|
||||||
|
@ -609,14 +611,14 @@ static LLVM_SETJMP: &str = "llvm.eh.sjlj.setjmp";
|
||||||
pub static LLVM_LONGJMP: &str = "llvm.eh.sjlj.longjmp";
|
pub static LLVM_LONGJMP: &str = "llvm.eh.sjlj.longjmp";
|
||||||
|
|
||||||
const LLVM_ADD_WITH_OVERFLOW: IntrinsicName =
|
const LLVM_ADD_WITH_OVERFLOW: IntrinsicName =
|
||||||
int_intrinsic!("llvm.sadd.with.overflow", "llvm.uadd.with.overflow");
|
llvm_int_intrinsic!("llvm.sadd.with.overflow", "llvm.uadd.with.overflow");
|
||||||
const LLVM_SUB_WITH_OVERFLOW: IntrinsicName =
|
const LLVM_SUB_WITH_OVERFLOW: IntrinsicName =
|
||||||
int_intrinsic!("llvm.ssub.with.overflow", "llvm.usub.with.overflow");
|
llvm_int_intrinsic!("llvm.ssub.with.overflow", "llvm.usub.with.overflow");
|
||||||
const LLVM_MUL_WITH_OVERFLOW: IntrinsicName =
|
const LLVM_MUL_WITH_OVERFLOW: IntrinsicName =
|
||||||
int_intrinsic!("llvm.smul.with.overflow", "llvm.umul.with.overflow");
|
llvm_int_intrinsic!("llvm.smul.with.overflow", "llvm.umul.with.overflow");
|
||||||
|
|
||||||
const LLVM_ADD_SATURATED: IntrinsicName = int_intrinsic!("llvm.sadd.sat", "llvm.uadd.sat");
|
const LLVM_ADD_SATURATED: IntrinsicName = llvm_int_intrinsic!("llvm.sadd.sat", "llvm.uadd.sat");
|
||||||
const LLVM_SUB_SATURATED: IntrinsicName = int_intrinsic!("llvm.ssub.sat", "llvm.usub.sat");
|
const LLVM_SUB_SATURATED: IntrinsicName = llvm_int_intrinsic!("llvm.ssub.sat", "llvm.usub.sat");
|
||||||
|
|
||||||
fn add_intrinsic<'ctx>(
|
fn add_intrinsic<'ctx>(
|
||||||
module: &Module<'ctx>,
|
module: &Module<'ctx>,
|
||||||
|
@ -2921,7 +2923,7 @@ pub fn complex_bitcast<'ctx>(
|
||||||
|
|
||||||
/// Check the size of the input and output types. Pretending we have more bytes at a pointer than
|
/// Check the size of the input and output types. Pretending we have more bytes at a pointer than
|
||||||
/// we actually do can lead to faulty optimizations and weird segfaults/crashes
|
/// we actually do can lead to faulty optimizations and weird segfaults/crashes
|
||||||
fn complex_bitcast_check_size<'a, 'ctx, 'env>(
|
pub fn complex_bitcast_check_size<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
from_value: BasicValueEnum<'ctx>,
|
from_value: BasicValueEnum<'ctx>,
|
||||||
to_type: BasicTypeEnum<'ctx>,
|
to_type: BasicTypeEnum<'ctx>,
|
||||||
|
@ -4073,7 +4075,13 @@ pub fn build_procedures_return_main<'a, 'ctx, 'env>(
|
||||||
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
procedures: MutMap<(Symbol, ProcLayout<'a>), roc_mono::ir::Proc<'a>>,
|
||||||
entry_point: EntryPoint<'a>,
|
entry_point: EntryPoint<'a>,
|
||||||
) -> (&'static str, FunctionValue<'ctx>) {
|
) -> (&'static str, FunctionValue<'ctx>) {
|
||||||
let mod_solutions = build_procedures_help(env, opt_level, procedures, entry_point, None);
|
let mod_solutions = build_procedures_help(
|
||||||
|
env,
|
||||||
|
opt_level,
|
||||||
|
procedures,
|
||||||
|
entry_point,
|
||||||
|
Some(Path::new("/tmp/test.ll")),
|
||||||
|
);
|
||||||
|
|
||||||
promote_to_main_function(env, mod_solutions, entry_point.symbol, entry_point.layout)
|
promote_to_main_function(env, mod_solutions, entry_point.symbol, entry_point.layout)
|
||||||
}
|
}
|
||||||
|
@ -5680,7 +5688,8 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos
|
NumAbs | NumNeg | NumRound | NumSqrtUnchecked | NumLogUnchecked | NumSin | NumCos
|
||||||
| NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin => {
|
| NumCeiling | NumFloor | NumToFloat | NumIsFinite | NumAtan | NumAcos | NumAsin
|
||||||
|
| NumToIntChecked => {
|
||||||
debug_assert_eq!(args.len(), 1);
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
||||||
let (arg, arg_layout) = load_symbol_and_layout(scope, &args[0]);
|
let (arg, arg_layout) = load_symbol_and_layout(scope, &args[0]);
|
||||||
|
@ -5692,7 +5701,14 @@ fn run_low_level<'a, 'ctx, 'env>(
|
||||||
match arg_builtin {
|
match arg_builtin {
|
||||||
Int(int_width) => {
|
Int(int_width) => {
|
||||||
let int_type = convert::int_type_from_int_width(env, *int_width);
|
let int_type = convert::int_type_from_int_width(env, *int_width);
|
||||||
build_int_unary_op(env, arg.into_int_value(), int_type, op, layout)
|
build_int_unary_op(
|
||||||
|
env,
|
||||||
|
arg.into_int_value(),
|
||||||
|
*int_width,
|
||||||
|
int_type,
|
||||||
|
op,
|
||||||
|
layout,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Float(float_width) => build_float_unary_op(
|
Float(float_width) => build_float_unary_op(
|
||||||
env,
|
env,
|
||||||
|
@ -6186,7 +6202,7 @@ impl RocReturn {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
enum CCReturn {
|
pub enum CCReturn {
|
||||||
/// Return as normal
|
/// Return as normal
|
||||||
Return,
|
Return,
|
||||||
/// require an extra argument, a pointer
|
/// require an extra argument, a pointer
|
||||||
|
@ -6228,7 +6244,7 @@ impl CCReturn {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// According to the C ABI, how should we return a value with the given layout?
|
/// According to the C ABI, how should we return a value with the given layout?
|
||||||
fn to_cc_return<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) -> CCReturn {
|
pub fn to_cc_return<'a, 'ctx, 'env>(env: &Env<'a, 'ctx, 'env>, layout: &Layout<'a>) -> CCReturn {
|
||||||
let return_size = layout.stack_size(env.target_info);
|
let return_size = layout.stack_size(env.target_info);
|
||||||
let pass_result_by_pointer = return_size > 2 * env.target_info.ptr_width() as u32;
|
let pass_result_by_pointer = return_size > 2 * env.target_info.ptr_width() as u32;
|
||||||
|
|
||||||
|
@ -6922,7 +6938,8 @@ fn int_type_signed_min(int_type: IntType) -> IntValue {
|
||||||
fn build_int_unary_op<'a, 'ctx, 'env>(
|
fn build_int_unary_op<'a, 'ctx, 'env>(
|
||||||
env: &Env<'a, 'ctx, 'env>,
|
env: &Env<'a, 'ctx, 'env>,
|
||||||
arg: IntValue<'ctx>,
|
arg: IntValue<'ctx>,
|
||||||
int_type: IntType<'ctx>,
|
arg_width: IntWidth,
|
||||||
|
arg_int_type: IntType<'ctx>,
|
||||||
op: LowLevel,
|
op: LowLevel,
|
||||||
return_layout: &Layout<'a>,
|
return_layout: &Layout<'a>,
|
||||||
) -> BasicValueEnum<'ctx> {
|
) -> BasicValueEnum<'ctx> {
|
||||||
|
@ -6933,11 +6950,11 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
|
||||||
match op {
|
match op {
|
||||||
NumNeg => {
|
NumNeg => {
|
||||||
// integer abs overflows when applied to the minimum value of a signed type
|
// integer abs overflows when applied to the minimum value of a signed type
|
||||||
int_neg_raise_on_overflow(env, arg, int_type)
|
int_neg_raise_on_overflow(env, arg, arg_int_type)
|
||||||
}
|
}
|
||||||
NumAbs => {
|
NumAbs => {
|
||||||
// integer abs overflows when applied to the minimum value of a signed type
|
// integer abs overflows when applied to the minimum value of a signed type
|
||||||
int_abs_raise_on_overflow(env, arg, int_type)
|
int_abs_raise_on_overflow(env, arg, arg_int_type)
|
||||||
}
|
}
|
||||||
NumToFloat => {
|
NumToFloat => {
|
||||||
// This is an Int, so we need to convert it.
|
// This is an Int, so we need to convert it.
|
||||||
|
@ -6956,6 +6973,75 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
|
||||||
"i64_to_f64",
|
"i64_to_f64",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
NumToIntChecked => {
|
||||||
|
// return_layout : Result N [ OutOfBounds ]* ~ { result: N, out_of_bounds: bool }
|
||||||
|
|
||||||
|
let target_int_width = match return_layout {
|
||||||
|
Layout::Struct(layouts) if layouts.len() == 2 => {
|
||||||
|
debug_assert!(matches!(layouts[1], Layout::Builtin(Builtin::Bool)));
|
||||||
|
match layouts[0] {
|
||||||
|
Layout::Builtin(Builtin::Int(iw)) => iw,
|
||||||
|
layout => internal_error!(
|
||||||
|
"There can only be an int layout here, found {:?}!",
|
||||||
|
layout
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
layout => internal_error!(
|
||||||
|
"There can only be a result layout here, found {:?}!",
|
||||||
|
layout
|
||||||
|
),
|
||||||
|
};
|
||||||
|
|
||||||
|
let arg_always_fits_in_target = (arg_width.stack_size() < target_int_width.stack_size()
|
||||||
|
&& (
|
||||||
|
// If the arg is unsigned, it will always fit in either a signed or unsigned
|
||||||
|
// int of a larger width.
|
||||||
|
!arg_width.is_signed()
|
||||||
|
||
|
||||||
|
// Otherwise if the arg is signed, it will always fit in a signed int of a
|
||||||
|
// larger width.
|
||||||
|
(target_int_width.is_signed() )
|
||||||
|
) )
|
||||||
|
|| // Or if the two types are the same, they trivially fit.
|
||||||
|
arg_width == target_int_width;
|
||||||
|
|
||||||
|
if arg_always_fits_in_target {
|
||||||
|
// This is guaranteed to succeed so we can just make it an int cast and let LLVM
|
||||||
|
// optimize it away.
|
||||||
|
let target_int_type = convert::int_type_from_int_width(env, target_int_width);
|
||||||
|
let target_int_val: BasicValueEnum<'ctx> = env
|
||||||
|
.builder
|
||||||
|
.build_int_cast(arg, target_int_type, "int_cast")
|
||||||
|
.into();
|
||||||
|
|
||||||
|
let return_type =
|
||||||
|
convert::basic_type_from_layout(env, return_layout).into_struct_type();
|
||||||
|
let r = return_type.const_zero();
|
||||||
|
let r = bd
|
||||||
|
.build_insert_value(r, target_int_val, 0, "converted_int")
|
||||||
|
.unwrap();
|
||||||
|
let r = bd
|
||||||
|
.build_insert_value(r, env.context.bool_type().const_zero(), 1, "out_of_bounds")
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
r.into_struct_value().into()
|
||||||
|
} else {
|
||||||
|
let bitcode_fn = if !arg_width.is_signed() {
|
||||||
|
// We are trying to convert from unsigned to signed/unsigned of same or lesser width, e.g.
|
||||||
|
// u16 -> i16, u16 -> i8, or u16 -> u8. We only need to check that the argument
|
||||||
|
// value fits in the MAX target type value.
|
||||||
|
&bitcode::NUM_INT_TO_INT_CHECKING_MAX[target_int_width][arg_width]
|
||||||
|
} else {
|
||||||
|
// We are trying to convert from signed to signed/unsigned of same or lesser width, e.g.
|
||||||
|
// i16 -> u16, i16 -> i8, or i16 -> u8. We need to check that the argument value fits in
|
||||||
|
// the MAX and MIN target type.
|
||||||
|
&bitcode::NUM_INT_TO_INT_CHECKING_MAX_AND_MIN[target_int_width][arg_width]
|
||||||
|
};
|
||||||
|
|
||||||
|
call_bitcode_fn_fixing_for_convention(env, &[arg.into()], return_layout, bitcode_fn)
|
||||||
|
}
|
||||||
|
}
|
||||||
_ => {
|
_ => {
|
||||||
unreachable!("Unrecognized int unary operation: {:?}", op);
|
unreachable!("Unrecognized int unary operation: {:?}", op);
|
||||||
}
|
}
|
||||||
|
|
|
@ -655,6 +655,9 @@ impl<'a> LowLevelCall<'a> {
|
||||||
_ => todo!("{:?}: {:?} -> {:?}", self.lowlevel, arg_type, ret_type),
|
_ => todo!("{:?}: {:?} -> {:?}", self.lowlevel, arg_type, ret_type),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
NumToIntChecked => {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
And => {
|
And => {
|
||||||
self.load_args(backend);
|
self.load_args(backend);
|
||||||
backend.code_builder.i32_and();
|
backend.code_builder.i32_and();
|
||||||
|
|
|
@ -111,6 +111,7 @@ pub enum LowLevel {
|
||||||
NumShiftRightBy,
|
NumShiftRightBy,
|
||||||
NumShiftRightZfBy,
|
NumShiftRightZfBy,
|
||||||
NumIntCast,
|
NumIntCast,
|
||||||
|
NumToIntChecked,
|
||||||
NumToStr,
|
NumToStr,
|
||||||
Eq,
|
Eq,
|
||||||
NotEq,
|
NotEq,
|
||||||
|
|
|
@ -1010,6 +1010,26 @@ define_builtins! {
|
||||||
122 NUM_MAX_U64: "maxU64"
|
122 NUM_MAX_U64: "maxU64"
|
||||||
123 NUM_MIN_I128: "minI128"
|
123 NUM_MIN_I128: "minI128"
|
||||||
124 NUM_MAX_I128: "maxI128"
|
124 NUM_MAX_I128: "maxI128"
|
||||||
|
125 NUM_TO_I8: "toI8"
|
||||||
|
126 NUM_TO_I8_CHECKED: "toI8Checked"
|
||||||
|
127 NUM_TO_I16: "toI16"
|
||||||
|
128 NUM_TO_I16_CHECKED: "toI16Checked"
|
||||||
|
129 NUM_TO_I32: "toI32"
|
||||||
|
130 NUM_TO_I32_CHECKED: "toI32Checked"
|
||||||
|
131 NUM_TO_I64: "toI64"
|
||||||
|
132 NUM_TO_I64_CHECKED: "toI64Checked"
|
||||||
|
133 NUM_TO_I128: "toI128"
|
||||||
|
134 NUM_TO_I128_CHECKED: "toI128Checked"
|
||||||
|
135 NUM_TO_U8: "toU8"
|
||||||
|
136 NUM_TO_U8_CHECKED: "toU8Checked"
|
||||||
|
137 NUM_TO_U16: "toU16"
|
||||||
|
138 NUM_TO_U16_CHECKED: "toU16Checked"
|
||||||
|
139 NUM_TO_U32: "toU32"
|
||||||
|
140 NUM_TO_U32_CHECKED: "toU32Checked"
|
||||||
|
141 NUM_TO_U64: "toU64"
|
||||||
|
142 NUM_TO_U64_CHECKED: "toU64Checked"
|
||||||
|
143 NUM_TO_U128: "toU128"
|
||||||
|
144 NUM_TO_U128_CHECKED: "toU128Checked"
|
||||||
}
|
}
|
||||||
2 BOOL: "Bool" => {
|
2 BOOL: "Bool" => {
|
||||||
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias
|
||||||
|
|
|
@ -984,7 +984,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
|
||||||
|
|
||||||
NumToStr | NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked
|
NumToStr | NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked
|
||||||
| NumRound | NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos
|
| NumRound | NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos
|
||||||
| NumAsin | NumIntCast => arena.alloc_slice_copy(&[irrelevant]),
|
| NumAsin | NumIntCast | NumToIntChecked => arena.alloc_slice_copy(&[irrelevant]),
|
||||||
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
|
||||||
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
|
||||||
|
|
|
@ -5251,4 +5251,27 @@ mod solve_expr {
|
||||||
"{ j : a, lst : List a, s : Str }",
|
"{ j : a, lst : List a, s : Str }",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn to_int() {
|
||||||
|
infer_eq_without_problem(
|
||||||
|
indoc!(
|
||||||
|
r#"
|
||||||
|
{
|
||||||
|
toI8: Num.toI8,
|
||||||
|
toI16: Num.toI16,
|
||||||
|
toI32: Num.toI32,
|
||||||
|
toI64: Num.toI64,
|
||||||
|
toI128: Num.toI128,
|
||||||
|
toU8: Num.toU8,
|
||||||
|
toU16: Num.toU16,
|
||||||
|
toU32: Num.toU32,
|
||||||
|
toU64: Num.toU64,
|
||||||
|
toU128: Num.toU128,
|
||||||
|
}
|
||||||
|
"#
|
||||||
|
),
|
||||||
|
r#"{ toI128 : Int * -> I128, toI16 : Int * -> I16, toI32 : Int * -> I32, toI64 : Int * -> I64, toI8 : Int * -> I8, toU128 : Int * -> U128, toU16 : Int * -> U16, toU32 : Int * -> U32, toU64 : Int * -> U64, toU8 : Int * -> U8 }"#,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2053,6 +2053,207 @@ fn max_u8() {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! to_int_tests {
|
||||||
|
($($fn:expr, $typ:ty, ($($test_name:ident, $input:expr, $output:expr $(, [ $($support_gen:literal),* ])? )*))*) => {$($(
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm", $($(feature = $support_gen)*)?))]
|
||||||
|
fn $test_name() {
|
||||||
|
let input = format!("{} {}", $fn, $input);
|
||||||
|
assert_evals_to!(&input, $output, $typ)
|
||||||
|
}
|
||||||
|
)*)*}
|
||||||
|
}
|
||||||
|
|
||||||
|
to_int_tests! {
|
||||||
|
"Num.toI8", i8, (
|
||||||
|
to_i8_same_width, "15u8", 15, ["gen-wasm"]
|
||||||
|
to_i8_truncate, "115i32", 115, ["gen-wasm"]
|
||||||
|
to_i8_truncate_wraps, "500i32", -12, ["gen-wasm"]
|
||||||
|
)
|
||||||
|
"Num.toI16", i16, (
|
||||||
|
to_i16_same_width, "15u16", 15, ["gen-wasm"]
|
||||||
|
to_i16_extend, "15i8", 15, ["gen-wasm"]
|
||||||
|
to_i16_truncate, "115i32", 115, ["gen-wasm"]
|
||||||
|
to_i16_truncate_wraps, "60000i32", -5536, ["gen-wasm"]
|
||||||
|
)
|
||||||
|
"Num.toI32", i32, (
|
||||||
|
to_i32_same_width, "15u32", 15, ["gen-wasm"]
|
||||||
|
to_i32_extend, "15i8", 15, ["gen-wasm"]
|
||||||
|
to_i32_truncate, "115i64", 115, ["gen-wasm"]
|
||||||
|
to_i32_truncate_wraps, "5000000000i64", 705032704, ["gen-wasm"]
|
||||||
|
)
|
||||||
|
"Num.toI64", i64, (
|
||||||
|
to_i64_same_width, "15u64", 15, ["gen-wasm"]
|
||||||
|
to_i64_extend, "15i8", 15, ["gen-wasm"]
|
||||||
|
to_i64_truncate, "115i128", 115
|
||||||
|
to_i64_truncate_wraps, "10_000_000_000_000_000_000i128", -8446744073709551616
|
||||||
|
)
|
||||||
|
"Num.toI128", i128, (
|
||||||
|
to_i128_same_width, "15u128", 15
|
||||||
|
to_i128_extend, "15i8", 15
|
||||||
|
)
|
||||||
|
"Num.toU8", u8, (
|
||||||
|
to_u8_same_width, "15i8", 15, ["gen-wasm"]
|
||||||
|
to_u8_truncate, "115i32", 115, ["gen-wasm"]
|
||||||
|
to_u8_truncate_wraps, "500i32", 244, ["gen-wasm"]
|
||||||
|
)
|
||||||
|
"Num.toU16", u16, (
|
||||||
|
to_u16_same_width, "15i16", 15, ["gen-wasm"]
|
||||||
|
to_u16_extend, "15i8", 15, ["gen-wasm"]
|
||||||
|
to_u16_truncate, "115i32", 115, ["gen-wasm"]
|
||||||
|
to_u16_truncate_wraps, "600000000i32", 17920, ["gen-wasm"]
|
||||||
|
)
|
||||||
|
"Num.toU32", u32, (
|
||||||
|
to_u32_same_width, "15i32", 15, ["gen-wasm"]
|
||||||
|
to_u32_extend, "15i8", 15, ["gen-wasm"]
|
||||||
|
to_u32_truncate, "115i64", 115, ["gen-wasm"]
|
||||||
|
to_u32_truncate_wraps, "5000000000000000000i64", 1156841472, ["gen-wasm"]
|
||||||
|
)
|
||||||
|
"Num.toU64", u64, (
|
||||||
|
to_u64_same_width, "15i64", 15, ["gen-wasm"]
|
||||||
|
to_u64_extend, "15i8", 15, ["gen-wasm"]
|
||||||
|
to_u64_truncate, "115i128", 115
|
||||||
|
to_u64_truncate_wraps, "10_000_000_000_000_000_000_000i128", 1864712049423024128
|
||||||
|
)
|
||||||
|
"Num.toU128", u128, (
|
||||||
|
to_u128_same_width, "15i128", 15
|
||||||
|
to_u128_extend, "15i8", 15
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! to_int_checked_tests {
|
||||||
|
($($fn:expr, $typ:ty, ($($test_name:ident, $input:expr, $output:expr)*))*) => {$($(
|
||||||
|
#[test]
|
||||||
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
|
fn $test_name() {
|
||||||
|
let sentinel = 23;
|
||||||
|
// Some n = Ok n, None = OutOfBounds
|
||||||
|
let expected = match $output.into() {
|
||||||
|
None => sentinel,
|
||||||
|
Some(n) => {
|
||||||
|
assert_ne!(n, sentinel);
|
||||||
|
n
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let input = format!("Result.withDefault ({} {}) {}", $fn, $input, sentinel);
|
||||||
|
assert_evals_to!(&input, expected, $typ)
|
||||||
|
}
|
||||||
|
)*)*}
|
||||||
|
}
|
||||||
|
|
||||||
|
to_int_checked_tests! {
|
||||||
|
"Num.toI8Checked", i8, (
|
||||||
|
to_i8_checked_same, "15i8", 15
|
||||||
|
to_i8_checked_same_width_unsigned_fits, "15u8", 15
|
||||||
|
to_i8_checked_same_width_unsigned_oob, "128u8", None
|
||||||
|
to_i8_checked_larger_width_signed_fits_pos, "15i16", 15
|
||||||
|
to_i8_checked_larger_width_signed_oob_pos, "128i16", None
|
||||||
|
to_i8_checked_larger_width_signed_fits_neg, "-15i16", -15
|
||||||
|
to_i8_checked_larger_width_signed_oob_neg, "-129i16", None
|
||||||
|
to_i8_checked_larger_width_unsigned_fits_pos, "15u16", 15
|
||||||
|
to_i8_checked_larger_width_unsigned_oob_pos, "128u16", None
|
||||||
|
)
|
||||||
|
"Num.toI16Checked", i16, (
|
||||||
|
to_i16_checked_smaller_width_pos, "15i8", 15
|
||||||
|
to_i16_checked_smaller_width_neg, "-15i8", -15
|
||||||
|
to_i16_checked_same, "15i16", 15
|
||||||
|
to_i16_checked_same_width_unsigned_fits, "15u16", 15
|
||||||
|
to_i16_checked_same_width_unsigned_oob, "32768u16", None
|
||||||
|
to_i16_checked_larger_width_signed_fits_pos, "15i32", 15
|
||||||
|
to_i16_checked_larger_width_signed_oob_pos, "32768i32", None
|
||||||
|
to_i16_checked_larger_width_signed_fits_neg, "-15i32", -15
|
||||||
|
to_i16_checked_larger_width_signed_oob_neg, "-32769i32", None
|
||||||
|
to_i16_checked_larger_width_unsigned_fits_pos, "15u32", 15
|
||||||
|
to_i16_checked_larger_width_unsigned_oob_pos, "32768u32", None
|
||||||
|
)
|
||||||
|
"Num.toI32Checked", i32, (
|
||||||
|
to_i32_checked_smaller_width_pos, "15i8", 15
|
||||||
|
to_i32_checked_smaller_width_neg, "-15i8", -15
|
||||||
|
to_i32_checked_same, "15i32", 15
|
||||||
|
to_i32_checked_same_width_unsigned_fits, "15u32", 15
|
||||||
|
to_i32_checked_same_width_unsigned_oob, "2147483648u32", None
|
||||||
|
to_i32_checked_larger_width_signed_fits_pos, "15i64", 15
|
||||||
|
to_i32_checked_larger_width_signed_oob_pos, "2147483648i64", None
|
||||||
|
to_i32_checked_larger_width_signed_fits_neg, "-15i64", -15
|
||||||
|
to_i32_checked_larger_width_signed_oob_neg, "-2147483649i64", None
|
||||||
|
to_i32_checked_larger_width_unsigned_fits_pos, "15u64", 15
|
||||||
|
to_i32_checked_larger_width_unsigned_oob_pos, "2147483648u64", None
|
||||||
|
)
|
||||||
|
"Num.toI64Checked", i64, (
|
||||||
|
to_i64_checked_smaller_width_pos, "15i8", 15
|
||||||
|
to_i64_checked_smaller_width_neg, "-15i8", -15
|
||||||
|
to_i64_checked_same, "15i64", 15
|
||||||
|
to_i64_checked_same_width_unsigned_fits, "15u64", 15
|
||||||
|
to_i64_checked_same_width_unsigned_oob, "9223372036854775808u64", None
|
||||||
|
to_i64_checked_larger_width_signed_fits_pos, "15i128", 15
|
||||||
|
to_i64_checked_larger_width_signed_oob_pos, "9223372036854775808i128", None
|
||||||
|
to_i64_checked_larger_width_signed_fits_neg, "-15i128", -15
|
||||||
|
to_i64_checked_larger_width_signed_oob_neg, "-9223372036854775809i128", None
|
||||||
|
to_i64_checked_larger_width_unsigned_fits_pos, "15u128", 15
|
||||||
|
to_i64_checked_larger_width_unsigned_oob_pos, "9223372036854775808u128", None
|
||||||
|
)
|
||||||
|
"Num.toI128Checked", i128, (
|
||||||
|
to_i128_checked_smaller_width_pos, "15i8", 15
|
||||||
|
to_i128_checked_smaller_width_neg, "-15i8", -15
|
||||||
|
to_i128_checked_same, "15i128", 15
|
||||||
|
to_i128_checked_same_width_unsigned_fits, "15u128", 15
|
||||||
|
to_i128_checked_same_width_unsigned_oob, "170141183460469231731687303715884105728u128", None
|
||||||
|
)
|
||||||
|
"Num.toU8Checked", u8, (
|
||||||
|
to_u8_checked_same, "15u8", 15
|
||||||
|
to_u8_checked_same_width_signed_fits, "15i8", 15
|
||||||
|
to_u8_checked_same_width_signed_oob, "-1i8", None
|
||||||
|
to_u8_checked_larger_width_signed_fits_pos, "15i16", 15
|
||||||
|
to_u8_checked_larger_width_signed_oob_pos, "256i16", None
|
||||||
|
to_u8_checked_larger_width_signed_oob_neg, "-1i16", None
|
||||||
|
to_u8_checked_larger_width_unsigned_fits_pos, "15u16", 15
|
||||||
|
to_u8_checked_larger_width_unsigned_oob_pos, "256u16", None
|
||||||
|
)
|
||||||
|
"Num.toU16Checked", u16, (
|
||||||
|
to_u16_checked_smaller_width_pos, "15i8", 15
|
||||||
|
to_u16_checked_smaller_width_neg_oob, "-15i8", None
|
||||||
|
to_u16_checked_same, "15u16", 15
|
||||||
|
to_u16_checked_same_width_signed_fits, "15i16", 15
|
||||||
|
to_u16_checked_same_width_signed_oob, "-1i16", None
|
||||||
|
to_u16_checked_larger_width_signed_fits_pos, "15i32", 15
|
||||||
|
to_u16_checked_larger_width_signed_oob_pos, "65536i32", None
|
||||||
|
to_u16_checked_larger_width_signed_oob_neg, "-1i32", None
|
||||||
|
to_u16_checked_larger_width_unsigned_fits_pos, "15u32", 15
|
||||||
|
to_u16_checked_larger_width_unsigned_oob_pos, "65536u32", None
|
||||||
|
)
|
||||||
|
"Num.toU32Checked", u32, (
|
||||||
|
to_u32_checked_smaller_width_pos, "15i8", 15
|
||||||
|
to_u32_checked_smaller_width_neg_oob, "-15i8", None
|
||||||
|
to_u32_checked_same, "15u32", 15
|
||||||
|
to_u32_checked_same_width_signed_fits, "15i32", 15
|
||||||
|
to_u32_checked_same_width_signed_oob, "-1i32", None
|
||||||
|
to_u32_checked_larger_width_signed_fits_pos, "15i64", 15
|
||||||
|
to_u32_checked_larger_width_signed_oob_pos, "4294967296i64", None
|
||||||
|
to_u32_checked_larger_width_signed_oob_neg, "-1i64", None
|
||||||
|
to_u32_checked_larger_width_unsigned_fits_pos, "15u64", 15
|
||||||
|
to_u32_checked_larger_width_unsigned_oob_pos, "4294967296u64", None
|
||||||
|
)
|
||||||
|
"Num.toU64Checked", u64, (
|
||||||
|
to_u64_checked_smaller_width_pos, "15i8", 15
|
||||||
|
to_u64_checked_smaller_width_neg_oob, "-15i8", None
|
||||||
|
to_u64_checked_same, "15u64", 15
|
||||||
|
to_u64_checked_same_width_signed_fits, "15i64", 15
|
||||||
|
to_u64_checked_same_width_signed_oob, "-1i64", None
|
||||||
|
to_u64_checked_larger_width_signed_fits_pos, "15i128", 15
|
||||||
|
to_u64_checked_larger_width_signed_oob_pos, "18446744073709551616i128", None
|
||||||
|
to_u64_checked_larger_width_signed_oob_neg, "-1i128", None
|
||||||
|
to_u64_checked_larger_width_unsigned_fits_pos, "15u128", 15
|
||||||
|
to_u64_checked_larger_width_unsigned_oob_pos, "18446744073709551616u128", None
|
||||||
|
)
|
||||||
|
"Num.toU128Checked", u128, (
|
||||||
|
to_u128_checked_smaller_width_pos, "15i8", 15
|
||||||
|
to_u128_checked_smaller_width_neg_oob, "-15i8", None
|
||||||
|
to_u128_checked_same, "15u128", 15
|
||||||
|
to_u128_checked_same_width_signed_fits, "15i128", 15
|
||||||
|
to_u128_checked_same_width_signed_oob, "-1i128", None
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(any(feature = "gen-llvm"))]
|
#[cfg(any(feature = "gen-llvm"))]
|
||||||
fn is_multiple_of() {
|
fn is_multiple_of() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue