Merge branch 'trunk' into drop-libcxx

This commit is contained in:
Richard Feldman 2021-08-20 07:42:46 -04:00 committed by GitHub
commit cd27c4c7d6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 364 additions and 7 deletions

View File

@ -79,6 +79,8 @@ comptime {
exportNumFn(num.powInt, "pow_int");
exportNumFn(num.acos, "acos");
exportNumFn(num.asin, "asin");
exportNumFn(num.bytesToU16C, "bytes_to_u16");
exportNumFn(num.bytesToU32C, "bytes_to_u32");
exportNumFn(num.round, "round");
}

View File

@ -1,6 +1,7 @@
const std = @import("std");
const always_inline = std.builtin.CallOptions.Modifier.always_inline;
const math = std.math;
const RocList = @import("list.zig").RocList;
pub fn atan(num: f64) callconv(.C) f64 {
return @call(.{ .modifier = always_inline }, math.atan, .{num});
@ -22,6 +23,24 @@ pub fn asin(num: f64) callconv(.C) f64 {
return @call(.{ .modifier = always_inline }, math.asin, .{num});
}
pub fn bytesToU16C(arg: RocList, position: usize) callconv(.C) u16 {
return @call(.{ .modifier = always_inline }, bytesToU16, .{ arg, position });
}
fn bytesToU16(arg: RocList, position: usize) u16 {
const bytes = @ptrCast([*]const u8, arg.bytes);
return @bitCast(u16, [_]u8{ bytes[position], bytes[position + 1] });
}
pub fn bytesToU32C(arg: RocList, position: usize) callconv(.C) u32 {
return @call(.{ .modifier = always_inline }, bytesToU32, .{ arg, position });
}
fn bytesToU32(arg: RocList, position: usize) u32 {
const bytes = @ptrCast([*]const u8, arg.bytes);
return @bitCast(u32, [_]u8{ bytes[position], bytes[position + 1], bytes[position + 2], bytes[position + 3] });
}
pub fn round(num: f64) callconv(.C) i64 {
return @floatToInt(i32, (@round(num)));
}

View File

@ -8,6 +8,8 @@ pub const NUM_ACOS: &str = "roc_builtins.num.acos";
pub const NUM_ATAN: &str = "roc_builtins.num.atan";
pub const NUM_IS_FINITE: &str = "roc_builtins.num.is_finite";
pub const NUM_POW_INT: &str = "roc_builtins.num.pow_int";
pub const NUM_BYTES_TO_U16: &str = "roc_builtins.num.bytes_to_u16";
pub const NUM_BYTES_TO_U32: &str = "roc_builtins.num.bytes_to_u32";
pub const NUM_ROUND: &str = "roc_builtins.num.round";
pub const STR_INIT: &str = "roc_builtins.str.init";

View File

@ -4,8 +4,8 @@ use roc_module::symbol::Symbol;
use roc_region::all::Region;
use roc_types::builtin_aliases::{
bool_type, dict_type, float_type, i128_type, int_type, list_type, nat_type, num_type,
ordering_type, result_type, set_type, str_type, str_utf8_byte_problem_type, u32_type, u64_type,
u8_type,
ordering_type, result_type, set_type, str_type, str_utf8_byte_problem_type, u16_type, u32_type,
u64_type, u8_type,
};
use roc_types::solved_types::SolvedType;
use roc_types::subs::VarId;
@ -501,6 +501,32 @@ pub fn types() -> MutMap<Symbol, (SolvedType, Region)> {
Box::new(float_type(flex(TVAR1))),
);
// bytesToU16 : List U8, Nat -> Result U16 [ OutOfBounds ]
{
let position_out_of_bounds = SolvedType::TagUnion(
vec![(TagName::Global("OutOfBounds".into()), vec![])],
Box::new(SolvedType::Wildcard),
);
add_top_level_function_type!(
Symbol::NUM_BYTES_TO_U16,
vec![list_type(u8_type()), nat_type()],
Box::new(result_type(u16_type(), position_out_of_bounds)),
);
}
// bytesToU32 : List U8, Nat -> Result U32 [ OutOfBounds ]
{
let position_out_of_bounds = SolvedType::TagUnion(
vec![(TagName::Global("OutOfBounds".into()), vec![])],
Box::new(SolvedType::Wildcard),
);
add_top_level_function_type!(
Symbol::NUM_BYTES_TO_U32,
vec![list_type(u8_type()), nat_type()],
Box::new(result_type(u32_type(), position_out_of_bounds)),
);
}
// Bool module
// and : Bool, Bool -> Bool

View File

@ -161,6 +161,8 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option<Def>
NUM_ATAN => num_atan,
NUM_ACOS => num_acos,
NUM_ASIN => num_asin,
NUM_BYTES_TO_U16 => num_bytes_to_u16,
NUM_BYTES_TO_U32 => num_bytes_to_u32,
NUM_MAX_INT => num_max_int,
NUM_MIN_INT => num_min_int,
NUM_BITWISE_AND => num_bitwise_and,
@ -1088,6 +1090,16 @@ fn num_asin(symbol: Symbol, var_store: &mut VarStore) -> Def {
)
}
/// Num.bytesToU16 : List U8, Nat -> Result U16 [ OutOfBounds ]
fn num_bytes_to_u16(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_bytes_to(symbol, var_store, 1, LowLevel::NumBytesToU16)
}
/// Num.bytesToU32 : List U8, Nat -> Result U32 [ OutOfBounds ]
fn num_bytes_to_u32(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_bytes_to(symbol, var_store, 3, LowLevel::NumBytesToU32)
}
/// Num.bitwiseAnd : Int a, Int a -> Int a
fn num_bitwise_and(symbol: Symbol, var_store: &mut VarStore) -> Def {
num_binop(symbol, var_store, LowLevel::NumBitwiseAnd)
@ -3359,6 +3371,97 @@ fn defn(
}
}
#[inline(always)]
fn num_bytes_to(symbol: Symbol, var_store: &mut VarStore, offset: i64, low_level: LowLevel) -> Def {
let len_var = var_store.fresh();
let list_var = var_store.fresh();
let elem_var = var_store.fresh();
let ret_var = var_store.fresh();
let bool_var = var_store.fresh();
let add_var = var_store.fresh();
let cast_var = var_store.fresh();
// Perform a bounds check. If it passes, run LowLevel::low_level
let body = If {
cond_var: bool_var,
branch_var: var_store.fresh(),
branches: vec![(
// if-condition
no_region(
// index + offset < List.len list
RunLowLevel {
op: LowLevel::NumLt,
args: vec![
(
len_var,
RunLowLevel {
op: LowLevel::NumAdd,
args: vec![
(add_var, Var(Symbol::ARG_2)),
(
add_var,
RunLowLevel {
ret_var: cast_var,
args: vec![(cast_var, Num(var_store.fresh(), offset))],
op: LowLevel::NumIntCast,
},
),
],
ret_var: add_var,
},
),
(
len_var,
RunLowLevel {
op: LowLevel::ListLen,
args: vec![(list_var, Var(Symbol::ARG_1))],
ret_var: len_var,
},
),
],
ret_var: bool_var,
},
),
// then-branch
no_region(
// Ok
tag(
"Ok",
vec![RunLowLevel {
op: low_level,
args: vec![
(list_var, Var(Symbol::ARG_1)),
(len_var, Var(Symbol::ARG_2)),
],
ret_var: elem_var,
}],
var_store,
),
),
)],
final_else: Box::new(
// else-branch
no_region(
// Err
tag(
"Err",
vec![tag("OutOfBounds", Vec::new(), var_store)],
var_store,
),
),
),
};
defn(
symbol,
vec![(list_var, Symbol::ARG_1), (len_var, Symbol::ARG_2)],
var_store,
body,
ret_var,
)
}
#[inline(always)]
fn defn_help(
fn_name: Symbol,

View File

@ -4746,6 +4746,42 @@ fn run_low_level<'a, 'ctx, 'env>(
}
}
}
NumBytesToU16 => {
debug_assert_eq!(args.len(), 2);
let list = load_symbol(scope, &args[0]).into_struct_value();
let position = load_symbol(scope, &args[1]);
call_bitcode_fn(
env,
&[
complex_bitcast(
env.builder,
list.into(),
env.context.i128_type().into(),
"to_i128",
),
position,
],
bitcode::NUM_BYTES_TO_U16,
)
}
NumBytesToU32 => {
debug_assert_eq!(args.len(), 2);
let list = load_symbol(scope, &args[0]).into_struct_value();
let position = load_symbol(scope, &args[1]);
call_bitcode_fn(
env,
&[
complex_bitcast(
env.builder,
list.into(),
env.context.i128_type().into(),
"to_i128",
),
position,
],
bitcode::NUM_BYTES_TO_U32,
)
}
NumCompare => {
use inkwell::FloatPredicate;

View File

@ -87,6 +87,8 @@ pub enum LowLevel {
NumAtan,
NumAcos,
NumAsin,
NumBytesToU16,
NumBytesToU32,
NumBitwiseAnd,
NumBitwiseXor,
NumBitwiseOr,
@ -122,10 +124,9 @@ impl LowLevel {
| NumRemUnchecked | NumIsMultipleOf | NumAbs | NumNeg | NumSin | NumCos
| NumSqrtUnchecked | NumLogUnchecked | NumRound | NumToFloat | NumPow | NumCeiling
| NumPowInt | NumFloor | NumIsFinite | NumAtan | NumAcos | NumAsin | NumBitwiseAnd
| NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy
| NumShiftRightZfBy | NumIntCast | Eq | NotEq | And | Or | Not | Hash | ExpectTrue => {
false
}
| NumBitwiseXor | NumBitwiseOr | NumShiftLeftBy | NumShiftRightBy | NumBytesToU16
| NumBytesToU32 | NumShiftRightZfBy | NumIntCast | Eq | NotEq | And | Or | Not
| Hash | ExpectTrue => false,
ListMap | ListMap2 | ListMap3 | ListMapWithIndex | ListKeepIf | ListWalk
| ListWalkUntil | ListWalkBackwards | ListKeepOks | ListKeepErrs | ListSortWith

View File

@ -891,7 +891,9 @@ define_builtins! {
100 NUM_AT_DECIMAL: "@Decimal"
101 NUM_DECIMAL: "Decimal" imported
102 NUM_DEC: "Dec" imported // the Num.Dectype alias
103 NUM_BYTES_TO_U16: "bytesToU16"
104 NUM_BYTES_TO_U32: "bytesToU32"
105 NUM_CAST_TO_NAT: "#castToNat"
}
2 BOOL: "Bool" => {
0 BOOL_BOOL: "Bool" imported // the Bool.Bool type alias

View File

@ -1009,6 +1009,8 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] {
NumAbs | NumNeg | NumSin | NumCos | NumSqrtUnchecked | NumLogUnchecked | NumRound
| NumCeiling | NumFloor | NumToFloat | Not | NumIsFinite | NumAtan | NumAcos | NumAsin
| NumIntCast => arena.alloc_slice_copy(&[irrelevant]),
NumBytesToU16 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
NumBytesToU32 => arena.alloc_slice_copy(&[borrowed, irrelevant]),
StrStartsWith | StrEndsWith => arena.alloc_slice_copy(&[owned, borrowed]),
StrStartsWithCodePt => arena.alloc_slice_copy(&[borrowed, irrelevant]),
StrFromUtf8 => arena.alloc_slice_copy(&[owned]),

View File

@ -1624,4 +1624,158 @@ mod gen_num {
// overflow
assert_evals_to!("Num.isMultipleOf -9223372036854775808 -1", true, bool);
}
#[test]
fn bytes_to_u16_clearly_out_of_bounds() {
assert_evals_to!(
indoc!(
r#"
bytes = Str.toUtf8 "hello"
when Num.bytesToU16 bytes 234 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
1,
u16
);
}
#[test]
fn bytes_to_u16_subtly_out_of_bounds() {
assert_evals_to!(
indoc!(
r#"
bytes = Str.toUtf8 "hello"
when Num.bytesToU16 bytes 4 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
1,
u16
);
}
#[test]
fn bytes_to_u32_clearly_out_of_bounds() {
assert_evals_to!(
indoc!(
r#"
bytes = Str.toUtf8 "hello"
when Num.bytesToU32 bytes 234 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
1,
u32
);
}
#[test]
fn bytes_to_u32_subtly_out_of_bounds() {
assert_evals_to!(
indoc!(
r#"
bytes = Str.toUtf8 "hello"
when Num.bytesToU32 bytes 2 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
1,
u32
);
}
#[test]
fn bytes_to_u16_max_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU16 [255, 255] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
65535,
u16
);
}
#[test]
fn bytes_to_u16_min_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU16 [0, 0] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
0,
u16
);
}
#[test]
fn bytes_to_u16_random_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU16 [164, 215] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
55_204,
u16
);
}
#[test]
fn bytes_to_u32_min_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU32 [0, 0, 0, 0] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
0,
u32
);
}
#[test]
fn bytes_to_u32_max_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU32 [255, 255, 255, 255] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
4_294_967_295,
u32
);
}
#[test]
fn bytes_to_u32_random_u8s() {
assert_evals_to!(
indoc!(
r#"
when Num.bytesToU32 [252, 124, 128, 121] 0 is
Ok v -> v
Err OutOfBounds -> 1
"#
),
2_038_463_740,
u32
);
}
}

View File

@ -521,6 +521,16 @@ pub fn u8_type() -> SolvedType {
)
}
#[inline(always)]
pub fn u16_type() -> SolvedType {
SolvedType::Alias(
Symbol::NUM_U16,
vec![],
vec![],
Box::new(int_alias_content(unsigned16_type())),
)
}
#[inline(always)]
pub fn binary64_type() -> SolvedType {
SolvedType::Alias(