Just wanted to give an update on the progress with purity inference upgrade for basic-cli PR#257
The host, platform, and all of the examples have been upgraded -- it's working well :smiley:
The following examples have a bug. I've logged issues for each of these.
$ roc examples/args.roc
thread 'main' panicked at crates/compiler/gen_llvm/src/llvm/build.rs:5748:19:
Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x11\x00\x00\x00\x00\x00\x00\x80\xe0{\x02\xeaI\xd4\xd7?"), definition of value binding ValueId(54): expected type 'union { (((heap_cell,), ()),), ((),) }', found type 'union { (), (), ((heap_cell,),), (), (), (), () }'
$ roc examples/task-list.roc
thread 'main' panicked at crates/compiler/gen_llvm/src/llvm/build.rs:5748:19:
Error in alias analysis: error in module ModName("UserApp"), function definition FuncName("\x11\x00\x00\x00\x04\x00\x00\x80\x16\x02|\xef\xcd\x95Y\xbd"), definition of value binding ValueId(42): expected type 'union { (union { ((heap_cell,), ()), (union { (), (), ((heap_cell,),), (), (), (), () },) },), ((),) }', found type 'union { ((heap_cell,), ()), (union { (), (), ((heap_cell,),), (), (), (), () },) }'
$ roc examples/tcp-client.roc
thread '<unnamed>' panicked at crates/compiler/mono/src/ir.rs:6191:56:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
I've also switched us over to the Weaver package and removed the Arg parser from basic-cli.
We can put this back if anyone has an issue with this, but we discussed eventually pointing people towards the packages as that keeps the platform simple, and I think now is a good time.
Next up is updating/checking all of the expect tests to get CI back online. I've also temporarily disabled the pull_request
trigger in the yaml files so I don't get spammed on every push, but we can restore this once we have that working locally.
I've also switched us over to the Weaver package and removed the Arg parser from basic-cli.
We can put this back if anyone has an issue with this, but we discussed eventually pointing people towards the packages as that keeps the platform simple, and I think now is a good time.
Could we stick to very simple arg handling like here and link to weaver in a comment? I want to avoid being blocked on needing to upgrade weaver in case of breaking roc changes.
That works :+1:
Thanks Luke!! I’ll look into the failing cases today
It looks like the tcp-client
example issue is caused by the return
issue that Sam is already looking into. I inlined all try
without using return
and it run properly:
Screenshot 2024-11-09 at 8.55.01 PM.png
@Luke Boswell so if you made an issue for that one, I think we can close it as a dupe
Actually, same with args.roc
checking task-list.roc
now
yep, same
so these are all down to the same issue :smile:
We should hold the release until we fix it, because not having try
is really painful
This is great news. Thanks for investigating those. :smiley:
No pressure @Sam Mohr :sweat_smile:
I'll look tonight. The fix is in theory ready to go in https://github.com/roc-lang/roc/pull/7204, but I'm hitting some weird codegen error
Once that's fixed, we're ready to go
--- STDOUT: test_gen::test_gen gen_return::early_return_annotated_function ---
running 1 test
--- STDERR: test_gen::test_gen gen_return::early_return_annotated_function ---
An internal compiler expectation was broken.
This is definitely a compiler bug.
Please file an issue here: <https://github.com/roc-lang/roc/issues/new/choose>
single register layouts are not complex symbols
Location: crates/compiler/gen_dev/src/generic64/x86_64.rs:355:17
Off to hang with a friend, I'll update when I get to a keyboard in like 5 hours or so
I think we're pretty much done.
If you are able, can you please give basic-cli purity inference upgrade a test, or to skim through the changes and see if we've missed anything obvious.
CI failures for native are to be expected until the new nightly which includes Sam's #7204 fix
The CI nix failures need investigation.. I'm not sure what's going wrong yet.
I'm hoping the euro-roc-stars can take it from here... I think I'm pretty much done for today. Still hopeful we can land this in time for AoC :fingers_crossed:
Ok, I've only started to investigate... but I think that latest fix @Sam Mohr or something has broken hello world on basic-cli purity inference.
It doesn't make any sense to me right now, but hello world exits with a non-zero exit code.
https://github.com/roc-lang/basic-cli/pull/257
Trying to figure out how that could happen. It's odd, that it's happening on literally the most simple program.
Uh oh
I can check it out in an hour when I get home
Hm... I think I found a workaround.
mainForHost! : I32 => Result {} I32
mainForHost! = \_ ->
when main! {} is
Ok {} -> Ok {}
Err (Exit code msg) ->
mainForHost! : I32 => Result {} I32
mainForHost! = \_ ->
main! {}
|> \result ->
when result is
Ok {} -> Ok {}
Okay, looking into it now
So when I log before and after running mainForHost!
in the Rust host code, I get this:
before running mainForHost!
Hello, World!
main! returned Ok {}
after mainForHost! exit_code = 1875841744
So this means we successfully run mainForHost!
and main!
inside it, but the exit code is wrong.
Not only that, but it's a seemingly random number each time
Which smells to me like we're reading past our memory when detecting the exit code
Okay, im tired and will pick this up tomorrow
is this a general bug or surgical linker specific
Also, this smells of an old bug we never figured out but seemed to have gone away
Im on mac, so it's happening with the legacy linker.
https://github.com/roc-lang/roc/issues/6121
We have some green shoots :tada:
Screenshot 2024-11-24 at 11.00.27.png
Now to figure out why linux isn't happy... anyone with a linux machine feel like helping? feel free to jump in
I can help in 2 or so hours
I found a bunch of places where the host fx type was returning a RocResult, but the PlatformTask was something like {} => {}
.
The current issue looks to be the non-zero exit code... and I suspect it may be related to the types for our Fx's not matching perfectly with what roc expects. My current hypothesis is that is we return a unit ()
to roc i.e. {}
in rust that maybe that is sized or aligned differently.
Need to dig into the LLVM IR maybe. Possible workaround might be to use a RocResult for everything across the host boundary which will ensure the correct alignment etc, and then just use a crash "unreachable"
in the platform for those effects that cannot fail (or rust host never provides an RocResult::Err(..) value).
Linux definitely seems to be more picky about this stuff than macos for some reason.
Do we generate size functions for fx's? It would be good to add a debug_assert that calls that and verifies the size of the return value in each effect is what roc expects.
I would expect more issues with roc result than without
Switching from effect to task (aka no result to result) lead to a lot of random issues
So I would try to avoid results where not needed
We do not generate size functions for effects
Linux probably isn't more picky. Probably would be an arm vs x86 c abi thing
I know we have bugs in our c abi handling
Also, unit is just void
So should be trivial and just always work
No alignment. No data at all
Something really fishy is going on... on this commit 86eca456539547b3720a0b1dc45ed967a6d76a7d
Just looking at the LLVM IR, not even the host, roc isn't generating anything for the effect PlatformTasks.currentArchOS!
, which is really strange. I modified hello-world.roc
to use this effect but for some reason roc isnt generating anything for it.
I've tried all kind of different things, like changing the interface, with and without Result, making it larger, random things... real head scratcher
@Brendan Hansknecht are you able to look at this? any thoughts on why this wouldn't be generating the effect?
I'll take a look tomorrow
Is the issue platform specific. Works for me
$ roc examples/hello-world.roc
{arch: AARCH64, os: MACOS}
[crates/roc_host/src/lib.rs:350:5] exit_code = 0
To get things building correctly, I had to rm platform/*.{a,rh}
then run ./jump-start.sh
again.
This is on latest main for roc.
Continues to just work when changing glue::ReturnArchOS
to only contain arch and os.
Was this your linux machine?
No, mac. Is it only broken on linux?
Yes, only broken on linux afaik
Ah, ok. Will check
Could be intel macs too though.
Should be fixed now
Issue was the return type for mainForHost not matching between Rust and Roc.
Thank you :partying_face:
Almost all passing. build-and-test-native
is failing with a segfault in examples/arg.roc
. Anyone have a native (not using nix) linux setup they can run the tests on? I assume running with valgrind will reveal the issue.
I guess this is probably a musl specific issue.
In a half hour, I can run on native 2021-era Fedora and/or Debian 12, though I may be unclear how to run it
Actually, I realized I can just download the nightly to get out of nix. Don't have to deal with all of the dependency and what not. So I can test.
Though I can't repro...
Just built everything outside of nix with roc nightly and musl, no segfault or failures.
So if anyone else wants to try and repro, that would be great.
Commands to repro on ubuntu: https://github.com/roc-lang/basic-cli/blob/purity-inference/.github/workflows/ci.yml
Everything is passing in nix, so I think this is the last piece to have fully working basic cli with purity inference.
Update: on a relatively clean native Debian 12 setup, I haven't figured out basic-cli local execution yet - debugging with Brendan in DMs suggestions welcome
After successful ./jump-start.sh
:
jan@lenny:~/_roc/basic-cli$ roc build.roc
LinkingStrategy was set to Surgical (default), but I tried to find the surgical host at any of these paths Either the generic host files or the surgical host files must exist. File status: Generic host (platform/host.rh): missing, Generic metadata (platform/metadata_host.rm): missing, Surgical host (platform/linux-x64.rh): missing, Surgical metadata (platform/metadata_linux-x64.rm): missing but it does not exist.
jan@lenny:~/_roc/basic-cli$ roc examples/hello-world.roc
LinkingStrategy was set to Surgical (default), but I tried to find the surgical host at any of these paths Either the generic host files or the surgical host files must exist. File status: Generic host (examples/../platform/host.rh): missing, Generic metadata (examples/../platform/metadata_host.rm): missing, Surgical host (examples/../platform/linux-x64.rh): missing, Surgical metadata (examples/../platform/metadata_linux-x64.rm): missing but it does not exist.
jan@lenny:~/_roc/basic-cli$
Strange. I thought roc build.roc
would have given you the surgical host... does it work if you add --linker legacy
?
Can you share $ ls -hl platform/
?
jan@lenny:~/_roc/basic-cli$ ls -hl platform/
total 15M
drwxr-xr-x 2 jan jan 4.0K Nov 30 14:38 Arg
-rw-r--r-- 1 jan jan 3.3K Nov 30 14:38 Arg.roc
-rw-r--r-- 1 jan jan 4.1K Nov 30 14:38 Cmd.roc
-rw-r--r-- 1 jan jan 2.6K Nov 30 14:38 Dir.roc
-rw-r--r-- 1 jan jan 4.5K Nov 30 14:38 EnvDecoding.roc
-rw-r--r-- 1 jan jan 7.2K Nov 30 14:38 Env.roc
-rw-r--r-- 1 jan jan 1.3K Nov 30 14:38 FileMetadata.roc
-rw-r--r-- 1 jan jan 9.8K Nov 30 14:38 File.roc
-rw-r--r-- 1 jan jan 399 Nov 30 14:38 glue.roc
-rw-r--r-- 1 jan jan 4.8K Nov 30 14:38 Http.roc
-rw-r--r-- 1 jan jan 813 Nov 30 14:38 InternalCommand.roc
-rw-r--r-- 1 jan jan 1.4K Nov 30 14:38 InternalFile.roc
-rw-r--r-- 1 jan jan 1.8K Nov 30 14:38 InternalHttp.roc
-rw-r--r-- 1 jan jan 3.6K Nov 30 14:38 InternalPath.roc
-rw-r--r-- 1 jan jan 263 Nov 30 14:38 libapp.roc
-rwxr-xr-x 1 jan jan 63K Nov 30 14:54 libapp.so
-rw-r--r-- 1 jan jan 14M Nov 30 14:54 libhost.a
-rw-r--r-- 1 jan jan 600 Nov 30 14:38 Locale.roc
-rw-r--r-- 1 jan jan 1.3K Nov 30 14:38 main.roc
-rw-r--r-- 1 jan jan 31K Nov 30 14:38 Path.roc
-rw-r--r-- 1 jan jan 3.1K Nov 30 14:38 PlatformTasks.roc
-rw-r--r-- 1 jan jan 310 Nov 30 14:38 Sleep.roc
-rw-r--r-- 1 jan jan 2.5K Nov 30 14:38 Stderr.roc
-rw-r--r-- 1 jan jan 4.1K Nov 30 14:38 Stdin.roc
-rw-r--r-- 1 jan jan 2.5K Nov 30 14:38 Stdout.roc
-rw-r--r-- 1 jan jan 6.4K Nov 30 14:38 Tcp.roc
-rw-r--r-- 1 jan jan 1.2K Nov 30 14:38 Tty.roc
-rw-r--r-- 1 jan jan 16K Nov 30 14:38 Url.roc
-rw-r--r-- 1 jan jan 2.4K Nov 30 14:38 Utc.roc
jan@lenny:~/_roc/basic-cli$
An you've ran roc build.roc
or just jumpstart?
Oops, yeah I wanted legacy, Brendan mentioned that
jan@lenny:~/_roc/basic-cli$ roc build.roc --linker=legacy
INFO: Checking provided roc; executing `roc version`:
roc nightly pre-release, built from commit d72da8e on Fr 29 Nov 2024 09:11:57 UTC
INFO: Getting the native operating system and architecture ...
INFO: Building stubbed app shared library ...
0 errors and 0 warnings found in 336 ms
while successfully building:
platform/libapp.so
INFO: Building rust host ...
warning: skipping duplicate package `host` found at `/home/jan/.cargo/git/checkouts/roc-f2007fde281427d6/8c73786/examples/glue/rust-platform`
warning: skipping duplicate package `host` found at `/home/jan/.cargo/git/checkouts/roc-f2007fde281427d6/8c73786/examples/cli/false-interpreter/platform`
warning: skipping duplicate package `host` found at `/home/jan/.cargo/git/checkouts/roc-f2007fde281427d6/8c73786/crates/glue/tests/fixture-templates/rust`
Finished `release` profile [optimized] target(s) in 0.37s
INFO: Failed to get env var CARGO_BUILD_TARGET with error VarNotFound. Assuming default CARGO_BUILD_TARGET (native)...
INFO: Moving the prebuilt binary from target/release/libhost.a to platform/linux-x64.a ...
INFO: Preprocessing surgical host ...
INFO: Successfully built platform files!
jan@lenny:~/_roc/basic-cli$
:tada: :question:
jan@lenny:~/_roc/basic-cli$ roc examples/hello-world.roc
Hello, World!
jan@lenny:~/_roc/basic-cli$
Nice
Another data point for defaulting to linker legacy if it's available and the flag isn't provided
I keep forgetting to make a PR for that
jan@lenny:~/_roc/basic-cli$ does that mean purity inference is fully working in basic-cli@0.17.0?
bash: does: command not found
jan@lenny:~/_roc/basic-cli$
:stuck_out_tongue_wink: but really though, happy to run any other commands to help validate PI-in-BC
You might need to install a few more deps, but can you run the CI test script
If you could work through the commands here, it would be useful validation: https://github.com/roc-lang/basic-cli/blob/purity-inference/.github/workflows/ci.yml
jan@lenny:~/_roc/basic-cli$ cat platform/src/lib.rs | grep -oP 'roc_fx_[^(\s]*' | sort | uniq -u | grep -q .
cat: platform/src/lib.rs: No such file or directory
jan@lenny:~/_roc/basic-cli$ curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-TESTING.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 9 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
curl: (22) The requested URL returned error: 404
jan@lenny:~/_roc/basic-cli$ curl -fOL https://github.com/roc-lang/roc/releases/download/nightly/roc_nightly-linux_x86_64-latest.tar.gz
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
100 34.1M 100 34.1M 0 0 14.0M 0 0:00:02 0:00:02 --:--:-- 21.2M
jan@lenny:~/_roc/basic-cli$ mv $(ls | grep "roc_nightly.*tar\.gz") roc_nightly.tar.gz
jan@lenny:~/_roc/basic-cli$ tar -xzf roc_nightly.tar.gz
jan@lenny:~/_roc/basic-cli$ rm roc_nightly.tar.gz
jan@lenny:~/_roc/basic-cli$ mv roc_nightly* roc_nightly
jan@lenny:~/_roc/basic-cli$ ./roc_nightly/roc version
roc nightly pre-release, built from commit d72da8e on Fr 29 Nov 2024 09:11:57 UTC
jan@lenny:~/_roc/basic-cli$ sudo apt install -y expect ncat
[sudo] password for jan:
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
liblua5.3-0 libtcl8.6 tcl-expect tcl8.6
Suggested packages:
tk8.6 tcl-tclreadline
The following NEW packages will be installed:
expect liblua5.3-0 libtcl8.6 ncat tcl-expect tcl8.6
0 upgraded, 6 newly installed, 0 to remove and 0 not upgraded.
Need to get 1,961 kB of archives.
After this operation, 6,186 kB of additional disk space will be used.
Get:1 http://deb.debian.org/debian bookworm/main amd64 libtcl8.6 amd64 8.6.13+dfsg-2 [1,035 kB]
Get:2 http://deb.debian.org/debian bookworm/main amd64 tcl8.6 amd64 8.6.13+dfsg-2 [120 kB]
Get:3 http://deb.debian.org/debian bookworm/main amd64 tcl-expect amd64 5.45.4-2+b1 [133 kB]
Get:4 http://deb.debian.org/debian bookworm/main amd64 expect amd64 5.45.4-2+b1 [166 kB]
Get:5 http://deb.debian.org/debian bookworm/main amd64 liblua5.3-0 amd64 5.3.6-2 [123 kB]
Get:6 http://deb.debian.org/debian bookworm/main amd64 ncat amd64 7.93+dfsg1-1 [384 kB]
Fetched 1,961 kB in 0s (6,665 kB/s)
Selecting previously unselected package libtcl8.6:amd64.
(Reading database ... 185306 files and directories currently installed.)
Preparing to unpack .../0-libtcl8.6_8.6.13+dfsg-2_amd64.deb ...
Unpacking libtcl8.6:amd64 (8.6.13+dfsg-2) ...
Selecting previously unselected package tcl8.6.
Preparing to unpack .../1-tcl8.6_8.6.13+dfsg-2_amd64.deb ...
Unpacking tcl8.6 (8.6.13+dfsg-2) ...
Selecting previously unselected package tcl-expect:amd64.
Preparing to unpack .../2-tcl-expect_5.45.4-2+b1_amd64.deb ...
Unpacking tcl-expect:amd64 (5.45.4-2+b1) ...
Selecting previously unselected package expect.
Preparing to unpack .../3-expect_5.45.4-2+b1_amd64.deb ...
Unpacking expect (5.45.4-2+b1) ...
Selecting previously unselected package liblua5.3-0:amd64.
Preparing to unpack .../4-liblua5.3-0_5.3.6-2_amd64.deb ...
Unpacking liblua5.3-0:amd64 (5.3.6-2) ...
Selecting previously unselected package ncat.
Preparing to unpack .../5-ncat_7.93+dfsg1-1_amd64.deb ...
Unpacking ncat (7.93+dfsg1-1) ...
Setting up libtcl8.6:amd64 (8.6.13+dfsg-2) ...
Setting up liblua5.3-0:amd64 (5.3.6-2) ...
Setting up tcl8.6 (8.6.13+dfsg-2) ...
Setting up tcl-expect:amd64 (5.45.4-2+b1) ...
Setting up ncat (7.93+dfsg1-1) ...
update-alternatives: using /usr/bin/ncat to provide /bin/nc (nc) in auto mode
Setting up expect (5.45.4-2+b1) ...
Processing triggers for man-db (2.11.2-2) ...
Processing triggers for libc-bin (2.36-9+deb12u9) ...
jan@lenny:~/_roc/basic-cli$ expect -v
expect version 5.45.4
jan@lenny:~/_roc/basic-cli$ ROC=./roc_nightly/roc EXAMPLES_DIR=./examples/ ./ci/all_tests.sh > pasteme.txt 2>&1
jan@lenny:~/_roc/basic-cli$
jan@lenny:~/_roc/basic-cli$ grep -zqv "crashed" pasteme.txt
jan@lenny:~/_roc/basic-cli$ sudo apt-get install -y musl-tools
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
The following additional packages will be installed:
musl musl-dev
Recommended packages:
linux-musl-dev
The following NEW packages will be installed:
musl musl-dev musl-tools
0 upgraded, 3 newly installed, 0 to remove and 0 not upgraded.
Need to get 1,036 kB of archives.
After this operation, 3,954 kB of additional disk space will be used.
Get:1 http://deb.debian.org/debian bookworm/main amd64 musl amd64 1.2.3-1 [406 kB]
Get:2 http://deb.debian.org/debian bookworm/main amd64 musl-dev amd64 1.2.3-1 [587 kB]
Get:3 http://deb.debian.org/debian bookworm/main amd64 musl-tools amd64 1.2.3-1 [42.3 kB]
Fetched 1,036 kB in 0s (3,959 kB/s)
Selecting previously unselected package musl:amd64.
(Reading database ... 185670 files and directories currently installed.)
Preparing to unpack .../musl_1.2.3-1_amd64.deb ...
Unpacking musl:amd64 (1.2.3-1) ...
Selecting previously unselected package musl-dev:amd64.
Preparing to unpack .../musl-dev_1.2.3-1_amd64.deb ...
Unpacking musl-dev:amd64 (1.2.3-1) ...
Selecting previously unselected package musl-tools.
Preparing to unpack .../musl-tools_1.2.3-1_amd64.deb ...
Unpacking musl-tools (1.2.3-1) ...
Setting up musl:amd64 (1.2.3-1) ...
Setting up musl-dev:amd64 (1.2.3-1) ...
Setting up musl-tools (1.2.3-1) ...
Processing triggers for man-db (2.11.2-2) ...
jan@lenny:~/_roc/basic-cli$ rustup target add x86_64-unknown-linux-musl
info: downloading component 'rust-std' for 'x86_64-unknown-linux-musl'
info: installing component 'rust-std' for 'x86_64-unknown-linux-musl'
33.0 MiB / 33.0 MiB (100 %) 15.0 MiB/s in 2s ETA: 0s
jan@lenny:~/_roc/basic-cli$ ROC=./roc_nightly/roc CARGO_BUILD_TARGET=x86_64-unknown-linux-musl ./roc_nightly/roc build.roc
INFO: Checking provided roc; executing `roc version`:
roc nightly pre-release, built from commit d72da8e on Fr 29 Nov 2024 09:11:57 UTC
INFO: Getting the native operating system and architecture ...
INFO: Building stubbed app shared library ...
0 errors and 0 warnings found in 425 ms
while successfully building:
platform/libapp.so
INFO: Building rust host ...
warning: skipping duplicate package `host` found at `/home/jan/.cargo/git/checkouts/roc-f2007fde281427d6/8c73786/examples/glue/rust-platform`
warning: skipping duplicate package `host` found at `/home/jan/.cargo/git/checkouts/roc-f2007fde281427d6/8c73786/examples/cli/false-interpreter/platform`
warning: skipping duplicate package `host` found at `/home/jan/.cargo/git/checkouts/roc-f2007fde281427d6/8c73786/crates/glue/tests/fixture-templates/rust`
Compiling libc v0.2.155
Compiling cfg-if v1.0.0
...
Compiling roc_host_bin v0.0.1 (/home/jan/_roc/basic-cli/crates/roc_host_bin)
Compiling host v0.0.1 (/home/jan/_roc/basic-cli/crates/roc_host_lib)
Finished `release` profile [optimized] target(s) in 31.42s
INFO: Moving the prebuilt binary from target/x86_64-unknown-linux-musl/release/libhost.a to platform/linux-x64.a ...
INFO: Preprocessing surgical host ...
INFO: Successfully built platform files!
jan@lenny:~/_roc/basic-cli$
jan@lenny:~/_roc/basic-cli$ NO_BUILD=1 ROC=./roc_nightly/roc EXAMPLES_DIR=./examples/ ./ci/all_tests.sh > pasteme2.txt 2>&1
jan@lenny:~/_roc/basic-cli$
jan@lenny:~/_roc/basic-cli$ grep -zqv "crashed" pasteme2.txt
jan@lenny:~/_roc/basic-cli$ I think I did it!
bash: I: command not found
jan@lenny:~/_roc/basic-cli$
Yay, but also :sad:
It's tricky to repro
So no repro yet? I don't think I saw a segfualt anywhere above. I wonder if it is Ubuntu 20.04 specific. @Anton any chance you can try to repro on something identical to CI?
Maybe I can just install Valgrind in CI and run with Valgrind?
I can't repro it on my linux machine running native either. I'm going to restart that CI and see if that changes anything
Is anyone hoping this goes green tonight to greenlight PI for AoC newcomers?
I'd like to have it done, but I don't think we're in any rush to upgrade it to the latest supported release. Maybe make a pre-release sometime soon for those game to use it and not wanting to build from source.
If we rush it, we're just likely to cause a lot of confusion and mess things up for people.
I can make a pre-release "purity-inference" version of my AoC template in case anyone wants to use it and builds basic-cli from source.
Added bonus if someone uses it and finds the bug that's blocking CI... we have a reproduction that we can glean some information from.
I don't think I can make the template work with purity inference. @Agus Zubiaga I might need your assistance to figure out if it's a me thing or something is broken
I think I'm stuck on this error
$ roc check examples/2020/01.roc
── MODULE PARAMS MISMATCH in examples/2020/01.roc ──────────────────────────────
Something is off with the params provided by this import:
9│> import aoc.AoC {
10│> stdin!: Stdin.bytes!,
11│> stdout!: Stdout.write!,
12│> time!: \{} -> Utc.now! {} |> Utc.toMillisSinceEpoch,
13│> }
This is the type I inferred:
{ stdin! : {} => Result (List U8) [
EndOfFile,
StdinErr [
AlreadyExists,
BrokenPipe,
Interrupted,
NotFound,
Other Str,
OutOfMemory,
PermissionDenied,
Unsupported,
],
], … }
However, AoC expects:
{ stdin! : effectful function, … }
────────────────────────────────────────────────────────────────────────────────
1 error and 4 warnings found in 20 ms
https://github.com/lukewilliamboswell/aoc-template/pull/5
Stripping it back to
module { stdin!, stdout!, time! } -> [Solution, solve!]
Solution err : {
year : U64,
day : U64,
title : Str,
part1 : Str -> Result Str err,
part2 : Str -> Result Str err,
} where err implements Inspect
solve! : Solution err => Result {} _
solve! = \{ year, day, title, part1, part2 } ->
Ok {}
I get the following error...
$ roc check examples/2020/01.roc
── MODULE PARAMS MISMATCH in examples/2020/01.roc ──────────────────────────────
Something is off with the params provided by this import:
9│> import aoc.AoC {
10│> stdin!: Stdin.bytes!,
11│> stdout!: Stdout.write!,
12│> time!: \{} -> Utc.now! {} |> Utc.toMillisSinceEpoch,
13│> }
This is the type I inferred:
{
stdin! : {} => Result (List U8) [
EndOfFile,
StdinErr [
AlreadyExists,
BrokenPipe,
Interrupted,
NotFound,
Other Str,
OutOfMemory,
PermissionDenied,
Unsupported,
],
],
stdout! : Str => Result {} [StdoutErr [
AlreadyExists,
BrokenPipe,
Interrupted,
NotFound,
Other Str,
OutOfMemory,
PermissionDenied,
Unsupported,
]],
time! : {}e => I128,
}
However, AoC expects:
{
stdin! : effectful function,
stdout! : effectful function,
time! : effectful function,
}
────────────────────────────────────────────────────────────────────────────────
1 error and 8 warnings found in 20 ms
Interesting. That looks like we’re not constraining the module with params enough. effectful function
means we only inferred it’s effectful from the name of the field, but we don’t know anything about the arguments or return type.
Unfortunately, I’m moving this whole weekend so I probably won’t have time to look at this until Monday
Are we planning to use purity inference for AoC?
No
Ok, good :sweat_smile:
I just jumped out of bed realizing everything I did above was on the main
branch and y'all were probably wondering about the purity-inference
branch.
@Brendan Hansknecht does this look more like what you expected?
[jan@framey basic-cli]$ git status
On branch purity-inference
Your branch is up to date with 'origin/purity-inference'.
nothing to commit, working tree clean
[jan@framey basic-cli]$ roc build.roc
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
Error:
Function, roc__mainForHost_0_caller, was not defined by the app.
Potential causes:
- because the platform was built with a non-compatible version of roc compared to the one you are running.
solutions:
+ Downgrade your roc version to the one that was used to build the platform.
+ Or ask the platform author to release a new version of the platform using a current roc release.
- This can also occur due to a bug in the compiler. In that case, file an issue here: https://github.com/roc-lang/roc/issues/new/choose
[jan@framey basic-cli]$
(on Fedora 40 now, not at my Debian 12 machine until tomorrow)
[jan@framey basic-cli]$ ./jump-start.sh
+ '[' -z '' ']'
+ echo 'Warning: ROC environment variable is not set... I'\''ll try with just '\''roc'\''.'
Warning: ROC environment variable is not set... I'll try with just 'roc'.
+ ROC=roc
+ roc build --lib ./platform/libapp.roc
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
0 errors and 0 warnings found in 360 ms
while successfully building:
./platform/libapp
+ cargo build --release
Finished `release` profile [optimized] target(s) in 0.16s
+ '[' -n '' ']'
+ cp target/release/libhost.a ./platform/libhost.a
+ roc build --linker=legacy build.roc
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
ld: platform/linux-x64.a(host-e4e7228faf4c51e3.host.62e8b11d478b1302-cgu.0.rcgu.o): in function `rust_main':
host.62e8b11d478b1302-cgu.0:(.text.rust_main+0x88): undefined reference to `roc__mainForHost_0_caller'
ld: host.62e8b11d478b1302-cgu.0:(.text.rust_main+0xc2): undefined reference to `roc__mainForHost_0_caller'
thread 'main' panicked at crates/compiler/build/src/program.rs:1058:17:
not yet implemented: linker failed with exit code Some(1)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
[jan@framey basic-cli]$ roc build.roc --linker=legacy
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
ld: platform/linux-x64.a(host-e4e7228faf4c51e3.host.62e8b11d478b1302-cgu.0.rcgu.o): in function `rust_main':
host.62e8b11d478b1302-cgu.0:(.text.rust_main+0x88): undefined reference to `roc__mainForHost_0_caller'
ld: host.62e8b11d478b1302-cgu.0:(.text.rust_main+0xc2): undefined reference to `roc__mainForHost_0_caller'
thread 'main' panicked at crates/compiler/build/src/program.rs:1058:17:
not yet implemented: linker failed with exit code Some(1)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
[jan@framey basic-cli]$
[jan@framey basic-cli]$ roc build.roc --linker=legacy
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
ld: platform/linux-x64.a(host-e4e7228faf4c51e3.host.62e8b11d478b1302-cgu.0.rcgu.o): in function `rust_main':
host.62e8b11d478b1302-cgu.0:(.text.rust_main+0x88): undefined reference to `roc__mainForHost_0_caller'
ld: host.62e8b11d478b1302-cgu.0:(.text.rust_main+0xc2): undefined reference to `roc__mainForHost_0_caller'
thread 'main' panicked at crates/compiler/build/src/program.rs:1058:17:
not yet implemented: linker failed with exit code Some(1)
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
[jan@framey basic-cli]$
That is likely different. Probably from a stale platform/linux_x64.a
(not sure I got that file name quite right). If you rm platform/*.a
then ./jump-start.sh
again,what do you get?
[jan@framey basic-cli]$ rm platform/*.a
[jan@framey basic-cli]$ ./jump-start.sh
+ '[' -z '' ']'
+ echo 'Warning: ROC environment variable is not set... I'\''ll try with just '\''roc'\''.'
Warning: ROC environment variable is not set... I'll try with just 'roc'.
+ ROC=roc
+ roc build --lib ./platform/libapp.roc
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
0 errors and 0 warnings found in 351 ms
while successfully building:
./platform/libapp
+ cargo build --release
Finished `release` profile [optimized] target(s) in 0.18s
+ '[' -n '' ']'
+ cp target/release/libhost.a ./platform/libhost.a
+ roc build --linker=legacy build.roc
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
0 errors and 0 warnings found in 986 ms
while successfully building:
build
[jan@framey basic-cli]$ roc build.roc
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
Error:
Function, roc__mainForHost_0_caller, was not defined by the app.
Potential causes:
- because the platform was built with a non-compatible version of roc compared to the one you are running.
solutions:
+ Downgrade your roc version to the one that was used to build the platform.
+ Or ask the platform author to release a new version of the platform using a current roc release.
- This can also occur due to a bug in the compiler. In that case, file an issue here: https://github.com/roc-lang/roc/issues/new/choose
[jan@framey basic-cli]$ roc build.roc --linker=legacy
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
INFO: Checking provided roc; executing `roc version`:
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
roc nightly pre-release, built from commit d72da8e on Fr 29 Nov 2024 09:11:57 UTC
INFO: Getting the native operating system and architecture ...
INFO: Building stubbed app shared library ...
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
0 errors and 0 warnings found in 949 ms
while successfully building:
platform/libapp.so
INFO: Building rust host ...
Finished `release` profile [optimized] target(s) in 0.20s
INFO: Failed to get env var CARGO_BUILD_TARGET with error VarNotFound. Assuming default CARGO_BUILD_TARGET (native)...
INFO: Moving the prebuilt binary from target/release/libhost.a to platform/linux-x64.a ...
INFO: Preprocessing surgical host ...
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
INFO: Successfully built platform files!
[jan@framey basic-cli]$ roc examples/hello-world.roc
roc: /lib64/libtinfo.so.6: no version information available (required by roc)
Hello, World!
[jan@framey basic-cli]$
I also looked into this, I was able to get purity inference basic cli to build fine (I had to use the jumpstart.s,) I'm on nixos.
I also tried fixing the AOC template:
I learned a few things
Looks like the above is green (on Fedora 40)
:salute: back to bed for me then
I fixed it!!
I assume the aoc template not the basic CLI issue in ci
Yeah, the template issue.
I'm working on making a test and also fixing the type checking issue .
The fix was:
// The existing effectul function unification function:
(EffectfulFunc, Func(args, closure, ret, fx)) => {
let mut outcome = unify_pool(env, pool, Variable::EFFECTFUL, *fx, ctx.mode);
outcome.union(merge(
env,
ctx,
Structure(Func(*args, *closure, *ret, Variable::EFFECTFUL)),
));
outcome
}
//I added this becasue it seemed to not be unifying properly and just leaving the function as effectful func so I assumed it never processed the oppisite case
(Func(args, closure, ret, fx), EffectfulFunc) => {
let mut outcome = unify_pool(env, pool, *fx, Variable::EFFECTFUL, ctx.mode);
outcome.union(merge(
env,
ctx,
Structure(Func(*args, *closure, *ret, Variable::EFFECTFUL)),
));
outcome
}
I think the current method for detecting whether a module should be using purity inference or Task effects is kind of janky and error prone.
For example if I require a record containing an effectful function it will break.
But given I assume tasks will be removed soon I suppose it's okay
Here is the PR fixing it:
https://github.com/roc-lang/roc/pull/7285
Awesome! Great catch
I guess the issue you found with the compiler not switching to purity inference mode was because you had no main.roc
, so it wasn’t finding a hosted
module on the platform with a !
-suffixed function when you roc check
a module
That’s a tricky case to detect reliably, because we need to switch to the right mode before canonicalization, but I don’t know if we have the all the context from just the parser
Maybe we can visit all the exprs looking for !
-suffixed defs and record fields (not call exprs because those could be tasks with the !
operator)
Using the platform is way easier to tell, but of course, we have no platform in this case
any chance you can try to repro on something identical to CI?
It's using a github CI machine, I'm just going to try calling valgrind in the ci workflow
+ valgrind ./examples//args div -n 5 -d 20
==5956== Memcheck, a memory error detector
==5956== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==5956== Using Valgrind-3.18.1 and LibVEX; rerun with -h for copyright info
==5956== Command: ./examples//args div -n 5 -d 20
==5956==
received argument: div
==5956== Can't extend stack to 0x4803138 during signal delivery for thread 1:
==5956== no stack segment
==5956==
==5956== Process terminating with default action of signal 11 (SIGSEGV)
==5956== Access not within mapped region at address 0x4803138
==5956== at 0x2C1045: ??? (in /home/runner/work/basic-cli/basic-cli/examples/args)
==5956== by 0x24AC63: ??? (in /home/runner/work/basic-cli/basic-cli/examples/args)
==5956== by 0x12950D: ??? (in /home/runner/work/basic-cli/basic-cli/examples/args)
==5956== by 0x480423F: ???
==5956== by 0x1294F2: ??? (in /home/runner/work/basic-cli/basic-cli/examples/args)
==5956== by 0x480423F: ???
==5956== by 0x12981C: ??? (in /home/runner/work/basic-cli/basic-cli/examples/args)
==5956== by 0x3E8: ???
==5956== by 0x7E: ???
==5956== If you believe this happened as a result of a stack
==5956== overflow in your program's main thread (unlikely but
==5956== possible), you can try to increase the size of the
==5956== main thread stack using the --main-stacksize= flag.
==5956== The main thread stack size used in this run was 16777216.
==5956==
==5956== HEAP SUMMARY:
==5956== in use at exit: 0 bytes in 0 blocks
==5956== total heap usage: 0 allocs, 0 frees, 0 bytes allocated
==5956==
==5956== All heap blocks were freed -- no leaks are possible
==5956==
==5956== For lists of detected and suppressed errors, rerun with: -s
==5956== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
./ci/all_tests.sh: line 60: 5956 Segmentation fault (core dumped) valgrind $EXAMPLES_DIR/args div -n 5 -d 20
I can try with the legacy linker tomorrow to clear up the ???
This could be #5924
You can probably see the valgrind errors locally
I made a small change and now I see __run_exit_handlers
and exit
in the valgrind output, so it is very likely #5924
btw if it is #5924, using the legacy linker will make the issue disappear
Agus Zubiaga said:
That’s a tricky case to detect reliably, because we need to switch to the right mode before canonicalization, but I don’t know if we have the all the context from just the parser
Does this, issue just kind of go away when we remove Tasks though? Like we can then not bother detecting if it's purity inference for tasks
As in, the current solution is imperfect but also very temporary, so it's not a big deal.
Yeah, totally
From my experience with #5924, we'll probably be good if we add or remove a roc_fx function from crates/roc_host/src/lib.rs, anybody any ideas for functionality we can add or leave out?
Lol, I didn't realise this could be a workaround. I could add something random in that we don't need pretty easily.
A minor addition would be adding https://doc.rust-lang.org/std/fs/fn.hard_link.html but something more substantial could be a semaphore.
I can start with hard linking, and then soft linking, and if those don't work we try something else?
@Luke Boswell thoughts?
Okay, I'll give that a go
Adding hard link doesn't seem to have worked
I'm thinking of merging @Eli Dowling's PR https://github.com/roc-lang/basic-cli/pull/278 into the PI branch. We don't have CI oversight here, but we haven't got everything passing yet anyway, and I think we should include the changes.
I'm not about the merge it rn, but if there's no objections I can do it tomorrow.
Good idea!
I'll review before then
I do think we should make ci/all_tests.sh pass on https://github.com/roc-lang/basic-cli/pull/278 before the merge, that just seems easier.
@Sam Mohr did you make much progress?
I am just looking at CI and it looks like we've progressed significantly, it's only failing in native now.
@Brendan Hansknecht or anyone familiar with valgrind... can we glean anything from this?
+ valgrind ./examples//args div -n 5 -d 20
==4550== Memcheck, a memory error detector
==4550== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==4550== Using Valgrind-3.15.0 and LibVEX; rerun with -h for copyright info
==4550== Command: ./examples//args div -n 5 -d 20
==4550==
received argument: div
==4550== Can't extend stack to 0x484c138 during signal delivery for thread 1:
==4550== no stack segment
==4550==
==4550== Process terminating with default action of signal 11 (SIGSEGV)
==4550== Access not within mapped region at address 0x484C138
==4550== at 0x2B0004: ??? (in /home/runner/work/basic-cli/basic-cli/examples/args)
==4550== by 0x48EE8A6: __run_exit_handlers (exit.c:108)
==4550== by 0x48EEA5F: exit (exit.c:139)
==4550== by 0x2556C4: ??? (in /home/runner/work/basic-cli/basic-cli/examples/args)
==4550== by 0x255653: ??? (in /home/runner/work/basic-cli/basic-cli/examples/args)
==4550== by 0x12859D: ??? (in /home/runner/work/basic-cli/basic-cli/examples/args)
==4550== If you believe this happened as a result of a stack
==4550== overflow in your program's main thread (unlikely but
==4550== possible), you can try to increase the size of the
==4550== main thread stack using the --main-stacksize= flag.
==4550== The main thread stack size used in this run was 16777216.
==4550== Invalid write of size 8
==4550== at 0x4831134: _vgnU_freeres (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_core-amd64-linux.so)
==4550== Address 0x484cff8 is on thread 1's stack
==4550==
==4550==
==4550== Process terminating with default action of signal 11 (SIGSEGV)
==4550== Access not within mapped region at address 0x484CFF8
==4550== at 0x4831134: _vgnU_freeres (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_core-amd64-linux.so)
==4550== If you believe this happened as a result of a stack
==4550== overflow in your program's main thread (unlikely but
==4550== possible), you can try to increase the size of the
==4550== main thread stack using the --main-stacksize= flag.
==4550== The main thread stack size used in this run was 16777216.
==4550==
==4550== HEAP SUMMARY:
==4550== in use at exit: 0 bytes in 0 blocks
==4550== total heap usage: 16 allocs, 16 frees, 3,330 bytes allocated
==4550==
==4550== All heap blocks were freed -- no leaks are possible
==4550==
==4550== For lists of detected and suppressed errors, rerun with: -s
==4550== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
./ci/all_tests.sh: line 60: 4550 Segmentation fault (core dumped) valgrind $EXAMPLES_DIR/args div -n 5 -d 20
Error: Process completed with exit code 139.
oh... I think I found something :fingers_crossed: that is just completely wrong. The args example doesn't do that anymore!
can you share what you found, I'm curious
https://github.com/roc-lang/basic-cli/pull/257/commits/cb9fface5c9aef5186b68d6e4be72e22ad36932f
We were calling the args example valgrind $EXAMPLES_DIR/args div -n 5 -d 20
but args has been dramatically simplified so we don't have a dependency on Weaver any more
Ah, maybe that was just our setup for investigating the segfault...
Added it back and getting the same segfault in CI. :sad:
I'm pretty sure the issue is related to ubuntu-20.04, I can't repro on my 22.04. I might try disabling that machine... just to confirm CI passes for 22.04 also.
Is the failure with both surgical and legacy linker? Can we change ci test check?
Ok, so using only legacy linker and valgrind is happy...
+ valgrind --main-stacksize=32777216 --track-origins=yes --leak-check=full --show-leak-kinds=all --verbose ./examples//args argument
==4639== Memcheck, a memory error detector
==4639== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==4639== Using Valgrind-3.15.0-608cb11914-20190413 and LibVEX; rerun with -h for copyright info
==4639== Command: ./examples//args argument
--4639-- Reading syms from /usr/lib/x86_64-linux-gnu/libc-2.31.so
--4639-- Considering /usr/lib/debug/.build-id/07/02430aef5fa3dda43986563e9ffcc47efbd75e.debug ..
--4639-- .. build-id is valid
--4639-- Reading syms from /usr/lib/x86_64-linux-gnu/libm-2.31.so
--4639-- Considering /usr/lib/debug/.build-id/8d/2573eff281739f0e2b0eb710c860ce0b7261cf.debug ..
--4639-- .. build-id is valid
--4639-- Reading syms from /usr/lib/x86_64-linux-gnu/libpthread-2.31.so
--4639-- Considering /usr/lib/debug/.build-id/9a/65bb469e45a1c6fbcffae5b82a2fd7a69eb479.debug ..
--4639-- .. build-id is valid
--4639-- Reading syms from /usr/lib/x86_64-linux-gnu/libdl-2.31.so
--4639-- Considering /usr/lib/debug/.build-id/25/372f43dbcc661aa02020d0365c948e89f6e612.debug ..
--4639-- .. build-id is valid
--4639-- Reading syms from /usr/lib/x86_64-linux-gnu/librt-2.31.so
--4639-- Considering /usr/lib/debug/.build-id/fc/7c873442781f08af6bc88f1acac7ecccec7285.debug ..
--4639-- .. build-id is valid
--4639-- Reading syms from /usr/lib/x86_64-linux-gnu/libutil-2.31.so
--4639-- Considering /usr/lib/debug/.build-id/24/4c5140c0521c81914afe5d92ad5ccc954857d5.debug ..
--4639-- .. build-id is valid
--4639-- Reading syms from /usr/lib/x86_64-linux-gnu/libgcc_s.so.1
--4639-- object doesn't have a symbol table
--4639-- REDIR: 0x48fa480 (libc.so.6:memmove) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f9780 (libc.so.6:strncpy) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa7b0 (libc.so.6:strcasecmp) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f90a0 (libc.so.6:strcat) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f97e0 (libc.so.6:rindex) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fbc50 (libc.so.6:rawmemchr) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x4916ce0 (libc.so.6:wmemchr) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x4916820 (libc.so.6:wcscmp) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa5e0 (libc.so.6:mempcpy) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa410 (libc.so.6:bcmp) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f9710 (libc.so.6:strncmp) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f9150 (libc.so.6:strcmp) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa540 (libc.so.6:memset) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x49167e0 (libc.so.6:wcschr) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f9670 (libc.so.6:strnlen) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f9230 (libc.so.6:strcspn) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa800 (libc.so.6:strncasecmp) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f91d0 (libc.so.6:strcpy) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa950 (libc.so.6:memcpy@@GLIBC_2.14) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x4917f50 (libc.so.6:wcsnlen) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x4916860 (libc.so.6:wcscpy) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f9820 (libc.so.6:strpbrk) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f9100 (libc.so.6:index) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f9630 (libc.so.6:strlen) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x4902bb0 (libc.so.6:memrchr) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa850 (libc.so.6:strcasecmp_l) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa3d0 (libc.so.6:memchr) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x4916930 (libc.so.6:wcslen) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48f9ae0 (libc.so.6:strspn) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa750 (libc.so.6:stpncpy) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa6f0 (libc.so.6:stpcpy) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fbc90 (libc.so.6:strchrnul) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x48fa8a0 (libc.so.6:strncasecmp_l) redirected to 0x48311d0 (_vgnU_ifunc_wrapper)
--4639-- REDIR: 0x49e2730 (libc.so.6:__strrchr_avx2) redirected to 0x483ea10 (rindex)
--4639-- REDIR: 0x48f40e0 (libc.so.6:malloc) redirected to 0x483b780 (malloc)
--4639-- REDIR: 0x49e2900 (libc.so.6:__strlen_avx2) redirected to 0x483ef40 (strlen)
--4639-- REDIR: 0x48f46d0 (libc.so.6:free) redirected to 0x483c9d0 (free)
--4639-- REDIR: 0x49de230 (libc.so.6:__strncmp_avx2) redirected to 0x483f670 (strncmp)
--4639-- REDIR: 0x48f5b10 (libc.so.6:calloc) redirected to 0x483dce0 (calloc)
received argument: argument
==4639==
==4639== HEAP SUMMARY:
==4639== in use at exit: 0 bytes in 0 blocks
==4639== total heap usage: 8 allocs, 8 frees, 1,320 bytes allocated
==4639==
==4639== All heap blocks were freed -- no leaks are possible
==4639==
==4639== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
But, now CI is stopping on another thing... so need to investigate that also. It may be related.
+ ./roc_nightly/roc dev ./examples/command.roc
BAZ=DUCK
FOO=BAR
FOO=BAR
EXEC
Error: Process completed with exit code 1.
Ok, we're down to the very last step, and we're failing specifically with x86_64-unknown-linux-musl and not getting any args... this is a known issue https://github.com/roc-lang/basic-cli/issues/82 or at least feels very familiar
Would we be happy if only the legacy linker was passing?
Yes
Things also make more sense now
Yep
There has been an uber long standing bug related to this. Sometimes it seemed to be surgical linker only. Other times it was clearly with the legacy linker. I thought we finally fixed it with the exit code fixed. I think it was actually two separate bugs. The exit code bug that affected both linkers and this bug which is surgical linker specific. Really intriguing it only affects the arg example. I wonder what makes that special. I guess arg is also the only example to break on musl so maybe how loading args happens is just brittle/more complex.
Arg is just the first example
I've been trying to get musl thing locally on my linux machine, and I can't even get rust to compile. I'm really confused
Is there something in particular I can help with?
I'm trying to get CI green, trying to figure out if we can keep the whole musl build and test thing
If you have a linux machine, you could try running that step manually (build and run the tests using musl)
For some reason that last CI run got stuck at an earlier point, just building the host using musl. The previous run got further and was stuck at running the examples. So not sure why that happened, seems odd -- because the things I changed between runs were only in the running part and shouldn't have affected the building.
Trying this now
Found something: So in rust staticlibs and cdylibs using musl do not support init functions. I'm not sure what all we interact with that uses the init array, but one piece that definitely uses the init array is args.
From what I can tell, we need get the args in main and then store them in a global or pass them into rust_main.
Ok, sounds easy enough to change
So I know it's a breaking change, but we're already requiring breaking for converting to main!
So the changes would be:
rust_main
rust_main
save it to a globalrust_main
I'll do that now as a helpful distraction from my musl pain
oh, no change to the roc main!
this is only the c program main that has to change
Can we change the signature of main!
to List Str => Result {} _
?
I know we don't have to do this
Also, I'm wanting to change PlatformTasks.roc
to Host.roc
... unless there's strong objection in the next few minutes :smiley:
But it would be a good change IMO
Do it Luke!
Use the force (of being first)
I prefer Effects.roc
but sure Host.roc
is fine too
I'm okay with either, but the benefit of Host.roc
is that it implies you're calling the host/platform
I was on the fence, I like Effects.roc
... but it's a hosted
module type (which I don't think we're removing anytime soon. So..
Effects.roc
feels like its good because it's plural and sounds like the old name
It's where I like to keep all the functions and types that cross the boundary across to the host
I mean it does hold all the effectful functions. also, why did we pick the name hosted
? I never got that
and either is fine luke. Pick what you like best
Also, no idea if fixing this will fix the valgrind bug with args and musl, but it seems likely
My thoughts are that Host
is clearer for someone not very familiar with how it all connects together
that is totally fair....HostedEffects.roc
.....nah, too long
Actually... I vaguely remember dealing with something about args and rust static libraries. There was a really deep rabbit hole I went down... trying to remember where that was.
Rust doesn't like putting a main
symbol in a static library
yep
That is why originally the lib version was wrapped by a c layer that was only a main function
It was something like https://doc.rust-lang.org/beta/unstable-book/language-features/start.html
That requires nightly though
I don't think that matters in this case:
this works just fine:
#[no_mangle]
pub extern "C" fn main(argc: isize, _argv: *const *const u8) -> i32 {
dbg!(argc);
roc_host::rust_main()
}
receives proper args
just wiring that up now
probably should just convert straight to a roc list of roc strings and pass that into roc_host::rust_main()
. Then can store that in a global for later retrieval, that or change to passing it into mainforhost
I'll reiterate my vote for the latter (passing args to main_for_host)
Though there is probably value in continuing to make them available by saving them in a https://doc.rust-lang.org/std/cell/struct.OnceCell.html
My current thoughts
use std::cell::RefCell;
thread_local! {
static ARGS: RefCell<Vec<String>> = const { RefCell::new(vec![]) };
}
pub fn with<T>(f: impl FnOnce(&mut Vec<String>) -> T) -> T {
ARGS.with(|args| f(&mut args.borrow_mut()))
}
#[no_mangle]
pub extern "C" fn main(argc: isize, argv: *const *const u8) {
roc_host::args::with(|args| {
for i in 0..argc {
unsafe {
let arg_ptr = *argv.offset(i);
let arg = std::ffi::CStr::from_ptr(arg_ptr as *const i8)
.to_string_lossy()
.into_owned();
args.push(arg);
}
}
});
roc_host::rust_main();
}
It shouldn't be thread_local!
, right?
It should just be static
#[no_mangle]
pub extern "C" fn roc_fx_args() -> RocList<RocStr> {
args::with(|args| {
let vec_args: Vec<RocStr> = args.into_iter().map(|s| s.as_str().into()).collect();
vec_args.as_slice().into()
})
}
rustc: `RefCell<Vec<String>>` cannot be shared between threads safely
the trait `Sync` is not implemented for `RefCell<Vec<String>>`
if you want to do aliasing and mutation between multiple threads, use `std::sync::RwLock` instead
shared static variables must have a type that implements `Sync`
you can store as a constant roc list
will be thread safe
rustc: cannot call non-const fn `RocList::<RocStr>::empty` in constants
calls in constants are limited to constant functions, tuple structs and tuple variants
How do I do that?
can make it read only and then convert it to a SendSafeRocList
change the glue to make the necessary RocList
functions constant
original_list.set_readonly()
SendSafeRocList(original_list)
@Brendan Hansknecht it might be easier if you put something together... I'm going in circles a bit here
Also, what are the thoughts on removing args and just passing the args into main? If we do that, we can skip all the globals in general?
I agree with Sam that it sounds pretty reasonable to just do
I suggested that... but I think Richard wanted to keep the API simpler
Thought that was in a Task based world... with PI I think it might be different
eh:
main! = \{} ->
Stdout.line! "Hello, World!"
vs
main! = \_args ->
Stdout.line! "Hello, World!"
We could have all the "global" environment type information passed in then, like the Env.platform! : {} => { arch : ARCH, os : OS }
instead
I think that is more useful as an effect
main! = \{args} ->
Stdout.line! "Got $(Inspect.toStr args)"
You could ignore all the stuff if you want and not destructure it
cause it may be wanted in a deeply nested state where it is convenient to just grab. Vs args are essentially only ever wanted at the top level
anyway, for now, let me try to fix without changing the api at all
So roc_std is missing some functionality that we need: https://github.com/roc-lang/roc/pull/7330
approved
git gud luke
I like to be thorough with my reviews :stuck_out_tongue:
rust traits are a pain even if they are technically safe........
just realized that Send
isn't enough, I need Sync
Technically a read only roc list is sync
but SendSafeRocList
is not Sync
cause it also includes unique roc lists, which are Send
but not Sync
So I also need ReadOnlyRocList/SyncSafeRocList
Can you rename it to SendAndSyncSafeRocList
? or DefinitelySendAndSyncSafeRocListBecauseItsReadOnly
It isn't SendAndSyncSafeRocList
cause a unique list is send but not sync. Only a readonly list is send and sync.
So they are two different wrapper types.
You could just store the args as Vec<String>
and convert every time, not as performant but it won't matter because it's just CLI args
I could also just wrap in a mutex
That'd be fine
RwLock is probably better here
Would allow for concurrent reads
That still needs Send
/Sync
let me make the proper fix, won't take long and is still wanted for other future usages.
ReadOnly types added to the PR: https://github.com/roc-lang/roc/pull/7330
Just got everything working locally with musl!!!
Will push to purity inference soon, though I guess https://github.com/roc-lang/roc/pull/7330 has to land first
Also, had to push one more fix around small strings to https://github.com/roc-lang/roc/pull/7330
Brendan Hansknecht said:
Will push to purity inference soon, though I guess https://github.com/roc-lang/roc/pull/7330 has to land first
Yeah, and then we need to add another commit with nix flake update
in it
Also, I'll be curious if everything works with both surgical and legacy linkers
Feel free to revert or remove all the hacks in ci/all_tests.sh
; we've left comments so it's easy to delete the relevant parts
It'd be really nice if this was the issue
from local testing, everything looks to be passing with both linkers for both musl and glibc
Wow, this is awesome.
I pushed my work, once https://github.com/roc-lang/roc/pull/7330 is landed, we can pull that in and cleanup any workarounds.
Thought it would be best to push now so either of you can take over later if I am not around
Thank you, Brendan!
I will be around
After Advent of Code ofc... :smiley:
Brendan Hansknecht said:
Also, what are the thoughts on removing args and just passing the args into main? If we do that, we can skip all the globals in general?
my original reasoning (although we never actually did this) is that the OS doesn't require that the args be valid UTF-8, and if you silently replace invalid UTF-8 sequences with the Unicode Replacement Character, then you've lost information and there becomes no way to access the actual original args that were passed into the process
but maybe for basic-cli
it's fine haha
perhaps a more advanced cli platform can offer that functionality
anyway, the main point is that THIS IS AWESOME!!!! :heart_eyes: :heart_eyes: :heart_eyes:
if you silently replace invalid UTF-8 sequences with the Unicode Replacement Character, then you've lost information
We were already doing this. Given we have ?
and try
now, maybe it would be best to change to returning a result.
but yeah, in practice, I would guess replacement is just fine for basic cli
yeah I assume that's what Java does
another thing we could do is to do that for you automatically, and then have a separate way to opt into going and getting the originals if you really want to
if there's demand in practice, we could introduce that as a nonbreaking change later
kk, I've kicked off CI now with the changes... and then I remember that we need to wait for the new nightly to land :tear:
@Anton we think this should be good to go. I've reverted the legacy linker and other debugging stuff we had in all_tests.sh
so should be just the nightly I think :fingers_crossed:
We don't need the new nightly
Just update the flake and the cargo lockfile
I can push the lockfile update if you want
I just pushed it I think
How many engineers to land a PR? :smiley:
Screenshot 2024-12-11 at 17.17.38.png
Been a massive effort, but feels like we're on the home stretch now.
Yeah, and if this passes ci with both the legacy and surgical linker, it will mean that we fixed some major platform bugs and don't have a hard to debug/fix surgical linker bug.
And we have :check: on native :tada:
Yay!
Can we pull legacy linker changes to the test script and run it with surgical then?
It's running with surgical now
Oh, epic!
Nix macos failed though... seems odd it passed for me locally, probably a CI thing
So that means it should be ready to land?
If the other nix machines pass, I'll restart it. I suspect it's a cache thing
I'll run locally in nix develop
to double check
Screenshot 2024-12-11 at 17.41.17.png
Getting close
Think it will pass on rerun?
Yeah, it's looking good
Screenshot 2024-12-11 at 17.53.24.png
Can anyone help a brother out?
@Sam Mohr how about one of those speedy reviews
Ok, I've left a few comments, nothing blocking in my opinion so I'm going to merge this. Thank you :smiley:
On it!
Not fast enough haha
Haha, too slow
This is awesome!! Good work to everyone who worked on this!
When will there be a release so it can be used without cloning the repo?
I'm going to bundle in the required changes for https://github.com/roc-lang/roc/pull/7321 in the next basic-cli release, that is a widely used function but probably few things will actually break because of that change. The new release will probably be available next week.
Cool, can't wait. For now I'll just clone then
Before the PI release, can we change main! : {} => Result {} _
to main! : List Str => Result {} _
to put all of the breaking changes together
Or did we not like the idea of that
From memory @Richard Feldman didn't like it
Sounds fine. Args seem fine to be main down
I personally don't want it any more complex than a list of strings.
Or maybe it was my larger suggestion of providing all the envrionment stuff in an record that could be destructured.
I think Richard commented about wanting it to be a zero arg function for max simplicity when teaching a new user. Though ignoring one arg sounds fine to teach a new user as well.
I can certainly make the change today. I would like to merge that refactor PR, and can add this in. I think were really close to getting cli/server/ssg all passong CI.
I might be getting ahead of myself, but I'm even thinking of bringing most of the basic-cli API across to ssg too.
*passing CI -- i mean ready for consumption
That's my thought. And I think every beginner will know what args are (they're what you pass to roc to use it!)
Args are always valid utf8? there's not strange OS things lurking there?
they don’t have to be utf8. they get be whatever at least on unix
They aren't, but we currently coerce them to UTF-8 anyway: https://github.com/roc-lang/basic-cli/blob/fbdd5e68fe286ae7f999d549d0e761df0b3773cf/crates/roc_host_bin/src/main.rs#L7
So we're already committed to adding just a bit of sanity
I think it is fine to default to utf-8 and if we really want, we can add a fallback when a user requests/needs it.
Basic CLI may turn out to be ok with coercion always
My concern is the silent nature of that, would people assume something and then it causes a bug. Maybe we detect non-utf8 args and crash, or instead provide zero args so it's easy to ignfore and then read args using an effect that gives List U8
.
Even Rust panics on non-UTF8 args: https://doc.rust-lang.org/beta/std/env/fn.args.html#panics
I'm down for the crash, but no one wants to handle non-UTF-8 except for a very small contingent
Yeah, I agree. I think crashing is the way to go.
Rust has args_os
as the fallback
We're aiming for higher level use cases. If someone needs non-utf8 args they're probably in the write or customise a platform space.
We still want to keep the effect for reading Args, so it's easier to get from within the app without having to thread apl the way through? Just checking.
I'd say yes
Eh
I would say pick just one way
Eh
I would say pick just one way
Eh
Both feels silly to me
I have bad Internet rn and I think Zulip does not like that
May as well try just passing it in, and see how that goes. It's easy enough to add back if people have issues.
Most cli apps are probably feeding it straight into weaver and parsing anyways
I'm working on this now
the only thing I strongly think we shouldn't do for sure is have the platform crash
based on what args are provided :laughing:
So we should continue to lossily convert them to UTF-8?
we could do that, or if we wanted to be fancy we could do like main : Result (List Str) [NonUnicodeArgs] -> ...
but basic-cli
probably shouldn't be fancy
I vote stupid
I'll change it back to lossy conversion
https://github.com/roc-lang/basic-cli/pull/289
the scenario I can think of where it could matter is that you have some non-utf8 filenames, and you want to pass those in as arguments
I'd say "don't do that" haha
the thing that bothers me about that is that we're now creating a situation where application authors are by default telling their users "don't have this use case"
This doesn't make basic-cli
unusable, it just means you need to pass the file via env var or something
That's fair
what will happen in practice is that people will use the most convenient API, namely the List Str
you get for free, and which doesn't handle this edge case, and then if end users have files they need to pass in that way, they just won't be able to do that
my hope with things like this is that if you're using a CLI arg parser like weaver anyway, then it can be actually very easy to handle that scenario
because weaver works on List U8
and provides a convenient interface to the app author anyway
Weaver uses List Str
and then if at some point I want to be like "oh just this one particular thing takes List U8
instead of Str
" then I only have to change it in one place
But we could change basic-cli
to use List (List U8)
and Weaver to do the same, and have Weaver handle encoding errors
oh, interesting...I mean, ideally it wouldn't :sweat_smile:
yeah, that! :point_up:
Okay, then I'll change this PR to make basic-cli do List (List U8)
and update Weaver later
that works for me! :thumbs_up:
For now, people can do validArgs = List.map args \arg -> Str.fromUtf8 arg |> Result.withDefault ""
And pass validArgs
to Weaver
@Richard Feldman currently, Arg.list! returns List Str
. Would you vote for:
Arg.list! : {} => List Str
and making it lossyArg.listBytes : {} => List (List U8)
Arg.list! : {} => List (List U8)
for Weaver compatSince apps will be handling stuff with Weaver primarily, I vote just changing Arg.list!
to {} => List (List U8)
Would it still be passed into main?
Or just the arg list effect?
It seems like Richard's main want is main! : List (List U8) => Result {} _
And to match with that, we should just convert Arg.list!
to {} => List (List U8)
Can we please remove arg list if we change main?
Brendan Hansknecht said:
Can we please remove arg list if we change main?
What if you want the arg list halfway through the program? Should you have to thread the args through the program?
I'm okay with that, just asking
I think it's fine. I personally just want one way. I think it is most common to use at the top, so forcing threading down sounds fine.
Okay, removing Arg.list!
yeah main! : List (List U8) => ...
sounds fine to me!
wait, crap
Windows :confounded:
on Windows they're U16
s
I'm gonna cry
There has to be a line somewhere
Do any other languages make users handle UTF-16?
C and C++ are main (int argc, char **argv)
I wonder if this use case was what caused Rust to introduce OsStr
That sounds exactly correct
yeah but they let you cast those pointers to u16 on Windows
Oh, I see
Why did Roc get rid of Nat?
that wouldn't help here
Presumably because the core language doesn't want to be OS-specific?
but the main reason was wanting to get to a state where no matter what target you run it on, all pure functions give the same answers given the same arguments
I agree it wouldn't help here
So OsStr would break that, right?
not necessarily, depends on the API
like OsStr.display : OsStr -> Str
would be fine
Or maybe roc-lang/path
also OsStr.to_list : OsStr -> [Windows (List U16), Unix (List U8)]
would be fine
Ok, if we can't crash (for reasons Richard outline above), what about my other option?
We check if it's valid UTF-8, if not, we provide an empty list for args. Then keep a Args.unix! : {} -> List (List U8)
and a Args.windows! : {} -> List (List U16)
.. at least until we implement roc-lang/path
That's probably just making things worse though... because people aren't going to pre-emptively check for empty args
So I also don't think that's a workable solution
What about unicode replacement characters?
That's the Rust approach
Richard Feldman said:
but the main reason was wanting to get to a state where no matter what target you run it on, all pure functions give the same answers given the same arguments
We could still maintain this property?
yeah we have that property now because we removed Nat
and this wouldn't change that
the only way to change that would be to change something in builtins, which this wouldn't do regardless :big_smile:
Luke Boswell said:
What about unicode replacement characters?
if we're ok with those, then we should just go back to main : List Str => ...
the downside there is that if you actually have an end user who has a file with invalid unicode in its path, and wants to pass it into a cli made with basic-cli
, it will be impossible
so basically if we go with the Unicode replacement character route, we're choosing to have those edge cases never work correctly in basic-cli
I dislike the silent nature of unicode replacment.. I'd prefer crashing and keeping main! : List Str => ...
which feels like a bit of a shame if people are just going to use a cli parser anyway, and could just say "hey this arg is a Path" (for example)
You'd probably have something like
OsStr := { bytes : List U8, os: [Unix, Windows] }
OsStr.bytes = os_str ->
when os is
Unix -> os_str.bytes
Windows ->
os_str.bytes
.chunks(2)
.map(left, right -> left.shift_left(8) + right)
if we crash, this statement is still equally true:
Richard Feldman said:
the downside there is that if you actually have an end user who has a file with invalid unicode in its path, and wants to pass it into a cli made with
basic-cli
, it will be impossible
Sam Mohr said:
You'd probably have something like
OsStr := { bytes : List U8, os: [Unix, Windows] } OsStr.bytes = os_str -> when os is Unix -> os_str.bytes Windows -> os_str.bytes .chunks(2) .map(left, right -> left.shift_left(8) + right)
Why not something like this https://github.com/roc-lang/path/blob/14b5fa518d13abb5012bcaa6809f7259e912eaeb/package/Path.roc#L24
That works too
yeah U16
differs in alignment too
Richard Feldman said:
the downside there is that if you actually have an end user who has a file with invalid unicode in its path, and wants to pass it into a cli made with
basic-cli
, it will be impossible
But in this case, people know they can't, and can reach for another program to convert the file names.
I don't know how niche a thing this is.
I'm sure it's very niche haha
Just a point of pedantry, a Path
is an OsStr
, but an argument is not a Path
necessarily, it's an OsStr
. So OsStr
is the real type at play
right
So I'm okay with main! : List (List U8) => Result {} _
because Weaver makes the user experience not really impeded, but if Weaver and basic-cli now depend of roc-lang/path
for some OsStr
type, now versioning is weird. This is a lot of weirdness eaten right at the start of "hello world" for a very niche problem
(not trying to do "hello world"-driven dev, just saying this would probably turn off beginners)
the problem with main! : List (List U8) => Result {} _
is that Windows will use u16s, not u8s
and weaver won't have any way of telling which one it's getting if we convert them all to u8
oh, here's a fairly straightforward idea
we could have main! : List Arg => Result {} _
Arg
works the same way as the OsStr
we've been talking about
but it's more self-descriptive and beginner-friendly imo
like "oh, this is a command-line arg"
"what can I do with one of those?"
and the most straightforward thing you can do is give them to Arg.weave
(or whatever)
Arg.display : Arg -> Result Str [InvalidUtf8]
yeah, exactly - stuff like that
How does Weaver engage with this? Does Weaver have basic-cli
as a dep?
although in this case it'd be InvalidUnicode
because on Windows they aren't UTF-8 :big_smile:
maybe the other way around
we'd previously talked about having basic-cli
ship with Weaver, right?
maybe this is a reason to do that
It used to until very recently
As embedded code
I'd be okay with that
Especially now that I'm not the only person with write access to Weaver
@Sam Mohr
For Weaver there should be a
Arg.to_raw : Arg -> [Unix (List U8), Windows (List U16)]
So that could be a module parameter for Weaver
I don't think it needs to be a module param
just pass it into weave
?
Weaver should just expose a module weaver.Arg
that exposes Arg
yeah but just seems simpler to have basic-cli
ship with it
then we have a coherent story around it
With Arg
you mean
"main gets a List Arg
and here is a nice way to turn that into a good experience for people"
Sam Mohr said:
How does Weaver engage with this? Does Weaver have
basic-cli
as a dep?
Then we're back here
I'd prefer we tell Weaver how to "decode" an Arg
that comes from the platform. Other people may come up with Weaver 2.0's in future
Module params are our friend
that works
Oh, I see!
heh, and actually in the static dispatch future, weave
can just ask for something that has a to_raw
method of the appropriate type :big_smile:
then it doesn't even need an extra arg!
Weaver has in Cli.roc:
module [parseArgs] { decodeArg : a -> Result WeaverArg _ }
WeaverArg : [Unix (List U8), Windows (List U16)]
parseArgs : List WeaverArg -> Result _ _
Maybe we should spin this off into another thread... but, would we be picking a name for that?
Like, anything with the shape to_os_str : thing -> [Unix _, Windows _] where ...
etc
I haven't fully digested static dispatch so ^^ may not be aligned with that worldview
Which for now would have people importing it as
import weaver.Cli { decodeArg : Arg.to_raw }
Not a huge fan of this, but it solves 100% of Bill Gates past mistakes
And it'll be better in static dispatch world
I like the idea of passing it to weave
as opposed to a module param
because that's the only place where it's relevant, and in the static dispatch world we can drop the extra weave
argument
Oh yeah, that works now, true
{ Cli.weave Args.to_os_raw <-
alpha: Opt.u64 { short: "a", help: "Set the alpha level" },
verbosity: Opt.count { short: "v", long: "verbose", help: "How loud we should be." },
files: Param.strList { name: "files", help: "The files to process." },
}
We have nice things now... I love roc :heart:
oh actually I think it might be finish
that needs it, not weave
Finish works
but yeah, one or the other
Since static dispatch doesn't allow implementing methods outside of the defining module for a type, I presume that once Weaver has converter your args to WeaverArg
, the user wouldn't be able to get a method on WeaverArg
to convert it to a roc-lang/path
data type.
But there's pass_to
So it's fine
yeah I think this plan should work!
In the long run, this kind of data type is in std or a parent lib, but we're way out from that, and this is still 90% as nice for the end user
Arg
and WeaverArg
can't be opaque
oh wait, parseOrDisplayMessage
has to be the answer :laughing:
that's the one that takes args
so it's the one that needs to know what to do with them
For args, why do we need to support utf16? All utf16 can be converted to utf-8. Why not just always losslessly convert to list u8?
in Windows the args aren't necessarily valid utf-16
and on Unix they aren't necessarily valid utf-8
Brendan rn
The only issue is with non Unicode args which is ever rarer
I need to read up more on os strings
yeah, the point is that if people are just going to be using Weaver anyway, we can handle those edge cases naturally
without people needing to know about all this
Yeah, we're not talking about path's any longer... this goes deep
in other words, we can create a pit of success for application authors
Richard has been down there, and survived to tell the story
Ah, windows is U16, but not necessarily utf16....why do os apis have to suck?
Brendan Hansknecht said:
Ah, windows is U16, but not necessarily utf16....why do os apis have to suck?
It's ok, @Sam Mohr has volunteered to handle all the edge cases in Weaver :cross:
Yes, that's the martyr emoji isn't it
So Weaver will implement utf16 so it can parse windows CLI args? And it will be able to parse both utf-8 and utf16. It also will be able to collect args that aren't valid utf-8/16 for use as paths and such
Is that the summary
haha, oh dear
I guess my biggest comment is that I really like being able to use simple args and keep the lie (of everything just working as utf-8). I find it very convenient for "basic" CLI. I personally don't generally use tools like Weaver (but maybe I should).
Maybe we want something to keep things more sane (only use the others for non-utf8 things)
Arg.as_os : Arg -> [ Utf8 Str, Unix (List U8), Windows (List U16) ]
Instead of handling every arg as Str
, it's gonna use WeaverArg
. For each arg, it'll attempt to parse to the right type from WeaverArg
, be that:
yeah I think we can offer a "quick and dirty" thing you can use e.g. if you're just making a quick script for yourself and don't want to build out a full set of args with help docs and all that
but have it be opt in
rather than part of of main
's type
Maybe Weaver could even offer that?
Which guides people away from saying "I'll just use the built-in thing"
also possible, but if I'm just making a quick script for myself and don't want to bother with edge cases, I probably don't want to bother with dependencies either :big_smile:
Agreed
Though you always need a platform
There's no such thing as a CLI in Roc without deps
sure
:shrug:
I just mean adding another one
I think API design decisions like this are really worthwhile
I just don't know what "quick and dirty args" will look like without giving users a footgun that this whole discussion just attempted to avoid
I'd be happy to see one, though!
So we have the "pit-of-success" but somewhat complicated default for main! : List Arg -> ...
, and then also provde a quick and dirty Arg.list! : {} => List Str
which might crash on invalid utf8
Maybe Arg.lossyList!
?
If we make Weaver part of basic CLI, I would be happy with it exposing a method to just convert all args to utf8
it's the difference between people being unpleasantly surprised when things don't work in unusual circumstances vs being like "hey the equivalent tool written in Roc works fine in this scenario, how come yours crashes, Non Roc Program?"
Otherwise yeah, effect is fine.
For the quick and dirty... you just ignore the args in main, and use the Effect
Richard Feldman said:
it's the difference between people being unpleasantly surprised when things don't work in unusual circumstances vs being like "hey the equivalent tool written in Roc works fine in this scenario, how come yours crashes, Non Roc Program?"
You really targeted my anti-Python CLI bias right there
or we could have Arg.display : Arg -> Str
"Ranger is the most popular TUI file manager, it's gotta be battle tested!"
And it is, but it still broke a bunch when I used it
and then you can just map
over them to get a List Str
also this discussion (and Path
) are good examples of why having a pit of success is so important
how many application authors are gonna go far enough down the OS rabbit hole to know about these things?
negative five percent
finding an API where if they use it and naturally end up with edge cases Just Working is awesome
Richard Feldman said:
or we could have
Arg.display : Arg -> Str
Yeah, I vote for this. Or if we want to be more explicit Arg.toStrLossy
Or similar name
Yep, having "lossy" in the name is really good IMO
C'mon guys... Arg.to_str_lossy
Yeah, I like where this design is reaching overall. Essentially main gets OsString args. Weaver knows how to handle them well. Otherwise, users can opt into the naive thing and the name calls out it is naive.
Steve Irwin over here with his love of reptiles
So I'm busy for 1.5 hours at work, I'll add Arg := [Unix (List U8), Windows (List U16)]
after that to the basic-cli PR
And then update Weaver later
While you're at it, feel like adding Str.from_utf16
to the builtins? :sweat_smile:
That'd be great
New thread time
That is also needed for js, right?
I feel like I remember this coming up in wasm land
Oh man, you're right, JS uses UTF-16 for strings
Yep, both JS and Dart used UCS-2 (and UTF16 in modern implementations)
Java too
although I think Java might use one of the custom encodings that disallows zeros, I forget
but yeah Str.from_utf16
seems like a good idea! :+1:
Great! Discussion happening here for the onlookers
👏🏼 love to see 242 unreads in a juicy topic
JanCVanB said:
👏🏼 love to see 242 unreads in a juicy topic
Make sure to wear a helmet
The new release will probably be available next week.
@Kilian Vounckx we're still getting in some final changes, so should be next week
No problems, take all the time you need
@Kilian Vounckx The pre-release for basic-cli 0.18.0 is availabe :)
Awesome! Thanks for the tag :big_smile:
Today I successfully manually merged that pre-release into my downstream "fork" roc-on - @Luke Boswell I loooooove the glue splitting, I refactored my codegen pipeline to populate a new crate roc_midi
Last updated: Jul 06 2025 at 12:14 UTC