diff --git a/crates/compiler/builtins/roc/Str.roc b/crates/compiler/builtins/roc/Str.roc index a5deb7e6e7..4ce043b1ce 100644 --- a/crates/compiler/builtins/roc/Str.roc +++ b/crates/compiler/builtins/roc/Str.roc @@ -221,3 +221,6 @@ toU16 : Str -> Result U16 [InvalidNumStr]* toI16 : Str -> Result I16 [InvalidNumStr]* toU8 : Str -> Result U8 [InvalidNumStr]* toI8 : Str -> Result I8 [InvalidNumStr]* + +## Gets the byte at the given index, without performing a bounds check +getUnsafe : Str, Nat -> U8 diff --git a/crates/compiler/builtins/src/bitcode.rs b/crates/compiler/builtins/src/bitcode.rs index ca7db04afb..798334d6cd 100644 --- a/crates/compiler/builtins/src/bitcode.rs +++ b/crates/compiler/builtins/src/bitcode.rs @@ -330,6 +330,7 @@ pub const STR_REPEAT: &str = "roc_builtins.str.repeat"; pub const STR_TRIM: &str = "roc_builtins.str.trim"; pub const STR_TRIM_LEFT: &str = "roc_builtins.str.trim_left"; pub const STR_TRIM_RIGHT: &str = "roc_builtins.str.trim_right"; +pub const STR_GET_UNSAFE: &str = "roc_builtins.str.get_unsafe"; pub const DICT_HASH: &str = "roc_builtins.dict.hash"; pub const DICT_HASH_STR: &str = "roc_builtins.dict.hash_str"; diff --git a/crates/compiler/can/src/builtins.rs b/crates/compiler/can/src/builtins.rs index 6d123a9a24..20301c0f10 100644 --- a/crates/compiler/can/src/builtins.rs +++ b/crates/compiler/can/src/builtins.rs @@ -74,6 +74,7 @@ pub fn builtin_defs_map(symbol: Symbol, var_store: &mut VarStore) -> Option STR_CONCAT => str_concat, STR_JOIN_WITH => str_join_with, STR_TO_SCALARS => str_to_scalars, + STR_GET_UNSAFE => str_get_unsafe, STR_SPLIT => str_split, STR_IS_EMPTY => str_is_empty, STR_STARTS_WITH => str_starts_with, @@ -1669,67 +1670,24 @@ fn str_concat(symbol: Symbol, var_store: &mut VarStore) -> Def { ) } +/// List.getUnsafe : Str, Nat -> U8 +fn str_get_unsafe(symbol: Symbol, var_store: &mut VarStore) -> Def { + lowlevel_2(symbol, LowLevel::StrGetUnsafe, var_store) +} + /// Str.toScalars : Str -> List U32 fn str_to_scalars(symbol: Symbol, var_store: &mut VarStore) -> Def { - let str_var = var_store.fresh(); - let list_u32_var = var_store.fresh(); - - let body = RunLowLevel { - op: LowLevel::StrToScalars, - args: vec![(str_var, Var(Symbol::ARG_1))], - ret_var: list_u32_var, - }; - - defn( - symbol, - vec![(str_var, Symbol::ARG_1)], - var_store, - body, - list_u32_var, - ) + lowlevel_1(symbol, LowLevel::StrToScalars, var_store) } /// Str.joinWith : List Str, Str -> Str fn str_join_with(symbol: Symbol, var_store: &mut VarStore) -> Def { - let list_str_var = var_store.fresh(); - let str_var = var_store.fresh(); - - let body = RunLowLevel { - op: LowLevel::StrJoinWith, - args: vec![ - (list_str_var, Var(Symbol::ARG_1)), - (str_var, Var(Symbol::ARG_2)), - ], - ret_var: str_var, - }; - - defn( - symbol, - vec![(list_str_var, Symbol::ARG_1), (str_var, Symbol::ARG_2)], - var_store, - body, - str_var, - ) + lowlevel_2(symbol, LowLevel::StrJoinWith, var_store) } /// Str.isEmpty : Str -> Bool fn str_is_empty(symbol: Symbol, var_store: &mut VarStore) -> Def { - let str_var = var_store.fresh(); - let bool_var = var_store.fresh(); - - let body = RunLowLevel { - op: LowLevel::StrIsEmpty, - args: vec![(str_var, Var(Symbol::ARG_1))], - ret_var: bool_var, - }; - - defn( - symbol, - vec![(str_var, Symbol::ARG_1)], - var_store, - body, - bool_var, - ) + lowlevel_1(symbol, LowLevel::StrIsEmpty, var_store) } /// Str.startsWith : Str, Str -> Bool diff --git a/crates/compiler/gen_llvm/src/llvm/build.rs b/crates/compiler/gen_llvm/src/llvm/build.rs index 5a6eaf33f1..7d3961dd56 100644 --- a/crates/compiler/gen_llvm/src/llvm/build.rs +++ b/crates/compiler/gen_llvm/src/llvm/build.rs @@ -5468,8 +5468,17 @@ fn run_low_level<'a, 'ctx, 'env>( list_prepend(env, original_wrapper, elem, elem_layout) } + StrGetUnsafe => { + // List.getUnsafe : List elem, Nat -> elem + debug_assert_eq!(args.len(), 2); + + let wrapper_struct = load_symbol(scope, &args[0]); + let elem_index = load_symbol(scope, &args[1]); + + call_bitcode_fn(env, &[wrapper_struct, elem_index], bitcode::STR_GET_UNSAFE) + } ListGetUnsafe => { - // List.get : List elem, Nat -> [Ok elem, OutOfBounds]* + // List.getUnsafe : List elem, Nat -> elem debug_assert_eq!(args.len(), 2); let (wrapper_struct, list_layout) = load_symbol_and_layout(scope, &args[0]); diff --git a/crates/compiler/gen_wasm/src/low_level.rs b/crates/compiler/gen_wasm/src/low_level.rs index d7dfeefd31..297e8b3ecf 100644 --- a/crates/compiler/gen_wasm/src/low_level.rs +++ b/crates/compiler/gen_wasm/src/low_level.rs @@ -218,6 +218,7 @@ impl<'a> LowLevelCall<'a> { // Str StrConcat => self.load_args_and_call_zig(backend, bitcode::STR_CONCAT), StrToScalars => self.load_args_and_call_zig(backend, bitcode::STR_TO_SCALARS), + StrGetUnsafe => self.load_args_and_call_zig(backend, bitcode::STR_GET_UNSAFE), StrJoinWith => self.load_args_and_call_zig(backend, bitcode::STR_JOIN_WITH), StrIsEmpty => match backend.storage.get(&self.arguments[0]) { StoredValue::StackMemory { location, .. } => { diff --git a/crates/compiler/module/src/low_level.rs b/crates/compiler/module/src/low_level.rs index d008fcf4b7..eea72f15b3 100644 --- a/crates/compiler/module/src/low_level.rs +++ b/crates/compiler/module/src/low_level.rs @@ -24,6 +24,7 @@ pub enum LowLevel { StrTrimRight, StrToNum, StrToScalars, + StrGetUnsafe, ListLen, ListWithCapacity, ListGetUnsafe, @@ -167,6 +168,7 @@ impl LowLevelWrapperType { match symbol { Symbol::STR_CONCAT => CanBeReplacedBy(StrConcat), + Symbol::STR_GET_UNSAFE => CanBeReplacedBy(StrGetUnsafe), Symbol::STR_TO_SCALARS => CanBeReplacedBy(StrToScalars), Symbol::STR_JOIN_WITH => CanBeReplacedBy(StrJoinWith), Symbol::STR_IS_EMPTY => CanBeReplacedBy(StrIsEmpty), diff --git a/crates/compiler/module/src/symbol.rs b/crates/compiler/module/src/symbol.rs index 4e2614d286..3dafe9eadd 100644 --- a/crates/compiler/module/src/symbol.rs +++ b/crates/compiler/module/src/symbol.rs @@ -1190,6 +1190,7 @@ define_builtins! { 32 STR_TO_U8: "toU8" 33 STR_TO_I8: "toI8" 34 STR_TO_SCALARS: "toScalars" + 35 STR_GET_UNSAFE: "getUnsafe" } 5 LIST: "List" => { 0 LIST_LIST: "List" imported // the List.List type alias diff --git a/crates/compiler/mono/src/borrow.rs b/crates/compiler/mono/src/borrow.rs index d3cc966c05..9f5968416e 100644 --- a/crates/compiler/mono/src/borrow.rs +++ b/crates/compiler/mono/src/borrow.rs @@ -892,7 +892,7 @@ pub fn lowlevel_borrow_signature(arena: &Bump, op: LowLevel) -> &[bool] { } ListWithCapacity => arena.alloc_slice_copy(&[irrelevant]), ListReplaceUnsafe => arena.alloc_slice_copy(&[owned, irrelevant, irrelevant]), - ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]), + StrGetUnsafe | ListGetUnsafe => arena.alloc_slice_copy(&[borrowed, irrelevant]), ListConcat => arena.alloc_slice_copy(&[owned, owned]), StrConcat => arena.alloc_slice_copy(&[owned, borrowed]), StrTrim => arena.alloc_slice_copy(&[owned]),