diff --git a/crates/compiler/builtins/roc/List.roc b/crates/compiler/builtins/roc/List.roc index 76447c10169..5cd38fed8bc 100644 --- a/crates/compiler/builtins/roc/List.roc +++ b/crates/compiler/builtins/roc/List.roc @@ -1182,8 +1182,17 @@ mapTry = \list, toResult -> Result.map (toResult elem) \ok -> List.append state ok -## This is the same as `iterate` but with [Result] instead of `[Continue, Break]`. -## Using `Result` saves a conditional in `mapTry`. +## Same as [List.walk], except you can stop walking early by returning `Err`. +## +## ## Performance Details +## +## Compared to [List.walk], this can potentially visit fewer elements (which can +## improve performance) at the cost of making each step take longer. +## However, the added cost to each step is extremely small, and can easily +## be outweighed if it results in skipping even a small number of elements. +## +## As such, it is typically better for performance to use this over [List.walk] +## if returning `Break` earlier than the last element is expected to be common. walkTry : List elem, state, (state, elem -> Result state err) -> Result state err walkTry = \list, init, func -> walkTryHelp list init func 0 (List.len list) diff --git a/crates/glue/src/RustGlue.roc b/crates/glue/src/RustGlue.roc index b58ab82dcbf..482e3a9047f 100644 --- a/crates/glue/src/RustGlue.roc +++ b/crates/glue/src/RustGlue.roc @@ -135,12 +135,11 @@ generateEntryPoint = \buf, types, name, id -> when Types.shape types id is Function rocFn -> arguments = - rocFn.args - |> List.mapWithIndex \argId, i -> + toArgStr rocFn.args types \argId, _shape, index -> type = typeName types argId - c = Num.toStr i - "arg\(c): \(type)" - |> Str.joinWith ", " + indexStr = Num.toStr index + + "arg\(indexStr): \(type)" ret = typeName types rocFn.ret @@ -154,16 +153,13 @@ generateEntryPoint = \buf, types, name, id -> when Types.shape types id is Function rocFn -> arguments = - rocFn.args - |> List.map \argId -> - shape = Types.shape types argId + toArgStr rocFn.args types \argId, shape, _index -> type = typeName types argId if canDeriveCopy types shape then "_: \(type)" else "_: &mut core::mem::ManuallyDrop<\(type)>" - |> Str.joinWith ", " ret = typeName types rocFn.ret "(_: *mut \(ret), \(arguments))" @@ -175,16 +171,13 @@ generateEntryPoint = \buf, types, name, id -> externArguments = when Types.shape types id is Function rocFn -> - rocFn.args - |> List.mapWithIndex \argId, index -> + toArgStr rocFn.args types \_argId, shape, index -> indexStr = Num.toStr index - shape = Types.shape types argId if canDeriveCopy types shape then "arg\(indexStr)" else "&mut core::mem::ManuallyDrop::new(arg\(indexStr))" - |> Str.joinWith ", " _ -> "" @@ -215,29 +208,40 @@ generateFunction = \buf, types, rocFn -> lambdaSet = typeName types rocFn.lambdaSet publicArguments = - rocFn.args - |> List.mapWithIndex \argId, i -> + toArgStr rocFn.args types \argId, _shape, index -> type = typeName types argId - c = Num.toStr i - "arg\(c): \(type)" - |> Str.joinWith ", " + indexStr = Num.toStr index + + "arg\(indexStr): \(type)" externDefArguments = - rocFn.args - |> List.mapWithIndex \argId, i -> - type = typeName types argId - c = Num.toStr i - "arg\(c): *const \(type)" - |> Str.joinWith ", " + withoutUnit = + toArgStr rocFn.args types \argId, _shape, index -> + type = typeName types argId + indexStr = Num.toStr index + + "arg\(indexStr): *const \(type)" + + if Str.isEmpty withoutUnit then + # These always have a first argument that's a pointer, even if it's to nothing. + "arg0: *const ()" + else + withoutUnit externCallArguments = - rocFn.args - |> List.mapWithIndex \_, i -> - c = Num.toStr i - "&arg\(c)" - |> Str.joinWith ", " + withoutUnit = + toArgStr rocFn.args types \_argId, _shape, index -> + indexStr = Num.toStr index + + "&arg\(indexStr)" - externComma = if Str.isEmpty publicArguments then "" else ", " + if Str.isEmpty withoutUnit then + # These always have a first argument that's a pointer, even if it's to nothing. + "&()" + else + withoutUnit + + publicComma = if Str.isEmpty publicArguments then "" else ", " ret = typeName types rocFn.ret @@ -251,20 +255,20 @@ generateFunction = \buf, types, rocFn -> } impl \(name) { - pub fn force_thunk(mut self, \(publicArguments)) -> \(ret) { + pub fn force_thunk(self\(publicComma)\(publicArguments)) -> \(ret) { extern "C" { - fn \(externName)(\(externDefArguments)\(externComma) closure_data: *mut u8, output: *mut \(ret)); + fn \(externName)(\(externDefArguments), closure_data: *mut u8, output: *mut \(ret)); } let mut output = core::mem::MaybeUninit::uninit(); - let ptr = &mut self.closure_data as *mut _ as *mut u8; + let closure_ptr = + (&mut core::mem::ManuallyDrop::new(self.closure_data)) as *mut _ as *mut u8; - unsafe { \(externName)(\(externCallArguments)\(externComma) ptr, output.as_mut_ptr(), ) }; - - // ownership of the closure is transferred back to roc - core::mem::forget(self.closure_data); + unsafe { + \(externName)(\(externCallArguments), closure_ptr, output.as_mut_ptr()); - unsafe { output.assume_init() } + output.assume_init() + } } } """ @@ -2065,3 +2069,33 @@ nextMultipleOf = \lhs, rhs -> when lhs % rhs is 0 -> lhs r -> lhs + (rhs - r) + + +isUnit : Shape -> Bool +isUnit = \shape -> + when shape is + Unit -> Bool.true + _ -> Bool.false + +toArgStr : List TypeId, Types, (TypeId, Shape, Nat -> Str) -> Str +toArgStr = \args, types, fmt -> + answer = List.walk args { state: "", index: 0 } \{ state, index }, argId -> + newState = + shape = Types.shape types argId + + # Drop `()` args; they aren't FFI-safe, and nothing will get passed anyway. + if isUnit shape then + state + else + argStr = fmt argId shape index + + if Str.isEmpty state then + argStr # Don't prepend a comma if this is the first one + else + state + |> Str.concat ", " + |> Str.concat argStr + + { state: newState, index: index + 1 } + + answer.state diff --git a/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs index 85af141dcbd..b3f6b9bf8db 100644 --- a/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/advanced-recursive-union/src/lib.rs @@ -7,7 +7,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = roc_app::mainForHost(()); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs b/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs index ff0293bf68d..decab793196 100644 --- a/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs +++ b/crates/glue/tests/fixtures/basic-recursive-union/src/lib.rs @@ -6,7 +6,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = roc_app::mainForHost(()); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs b/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs index 7cb25db13be..c99a7ae610b 100644 --- a/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs +++ b/crates/glue/tests/fixtures/nullable-wrapped/src/lib.rs @@ -14,7 +14,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = roc_app::mainForHost(()); + let tag_union = roc_app::mainForHost(); // Eq assert!(StrFingerTree::Empty() == StrFingerTree::Empty()); diff --git a/crates/glue/tests/fixtures/union-with-padding/src/lib.rs b/crates/glue/tests/fixtures/union-with-padding/src/lib.rs index 0c22b185fbe..c7a1a2d09bf 100644 --- a/crates/glue/tests/fixtures/union-with-padding/src/lib.rs +++ b/crates/glue/tests/fixtures/union-with-padding/src/lib.rs @@ -12,7 +12,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = roc_app::mainForHost(()); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits. diff --git a/crates/glue/tests/fixtures/union-without-padding/src/lib.rs b/crates/glue/tests/fixtures/union-without-padding/src/lib.rs index e4fb9a53047..1709441ebaa 100644 --- a/crates/glue/tests/fixtures/union-without-padding/src/lib.rs +++ b/crates/glue/tests/fixtures/union-without-padding/src/lib.rs @@ -10,7 +10,7 @@ pub extern "C" fn rust_main() -> i32 { use std::cmp::Ordering; use std::collections::hash_set::HashSet; - let tag_union = roc_app::mainForHost(()); + let tag_union = roc_app::mainForHost(); // Verify that it has all the expected traits.