Skip to content

Fix sqlite decoder inference - #475

Merged
lukewilliamboswell merged 2 commits into
roc-lang:mainfrom
Karakatiza666:fix-sqlite-decoder-inference
Aug 31, 2026
Merged

Fix sqlite decoder inference#475
lukewilliamboswell merged 2 commits into
roc-lang:mainfrom
Karakatiza666:fix-sqlite-decoder-inference

Conversation

@Karakatiza666

Copy link
Copy Markdown

The SQLite examples stop type-checking on any nightly from nightly-2026-08-30-34e7489 onward.

── ✗ missing method ─ examples/sqlite-basic.roc:65:8
This is trying to dispatch a method named find_first_index on an unresolved
type variable, but unresolved type variables have no methods.
    id = Sqlite.i64("id")(cols)(stmt)?

Same error at examples/sqlite-everything.roc:204 and :219.

Root cause

lookup_value! found a column with cols.find_first_index(...). The decoder combinators are deliberately unannotated, so the compiler has not worked out what cols is at that point, and it carries the question into whatever decoder an application writes, where it still cannot be answered. roc-lang/roc#10984 stopped those questions sharing an answer between calls, so a decoder with a name of its own asks about cols once per column and gets nowhere.

Only named decoders break. The inline rows: |cols| |stmt| ... form waits for query_many! to say what cols is, which is why the count is three and not more.

Applications cannot work around it. query_many! unwraps the opaque Stmt and hands the decoder a Host.SqliteStmt, and Host is not in the platform's exposes list, so there is no annotation an app is able to write.

Fix

  • call the builtin by name in lookup_value!: List.find_first_index(cols, ...). That says which type the lookup comes from, so cols settles as a List(Str) where it is asked about and nothing is left for the application to answer. platform/Path.roc:558 already reads the same lookup this way
  • add an expect covering a row decoder with a name of its own, which is the shape that breaks
  • move CI from nightly-2026-08-23-fb208ba to nightly-2026-08-30-34e7489. #10984 does not exist at the old pin, so neither the bug nor the test guarding it shows up there. I will drop bumping the nightly if you would rather move the pin separately.

No signature changes, so src/roc_platform_abi.rs and ci/regenerate_glue.sh are untouched: lookup_value! is module-private, sits outside the Sqlite :: [].{} block, and appears in no requires, provides or hosted block.

Verification

The test was checked against the bug on nightly-2026-08-30-34e7489:

platform/Sqlite.roc roc test platform/main.roc
this PR exit 0
fix reverted, test kept exit 1

Note that roc test prints All (208) tests passed even when the type error fires, so the exit code is the signal here, not the summary line.

./scripts/test.py was not run. The build and run stages, and the two test_spec.json cases per SQLite example, are for CI to confirm. The change is Roc-only and moves no program output.

roc fmt --check already fails on platform/Cmd.roc, platform/InternalSqlite.roc and platform/Url.roc on both nightlies, unrelated to this change and untouched by it.

Karakatiza666 added 2 commits August 31, 2026 09:41
`lookup_value!` found a column with `cols.find_first_index(...)`. The decoder
combinators are deliberately unannotated, so at that point the compiler has not
worked out what `cols` is, and asking for a method on it leaves a question the
compiler has to carry into whatever decoder an application writes. There it
still cannot be answered.

Since roc-lang/roc#10984 those questions no longer share an answer between
calls, so a decoder with a name of its own

    decode_todo = |cols|
        |stmt| {
            id = Sqlite.i64("id")(cols)(stmt)?
            task = Sqlite.str("task")(cols)(stmt)?
            status = Sqlite.str("status")(cols)(stmt)?
            ...

asks about `cols` three separate times and gets nowhere:

    ── ✗ missing method ─ examples/sqlite-basic.roc:65:8
    This is trying to dispatch a method named find_first_index on an
    unresolved type variable, but unresolved type variables have no methods.

Naming the builtin says which type the lookup comes from, which settles `cols`
as a `List(Str)` here and leaves nothing for the application to resolve. This is
already how Path.roc reads the same lookup.

Add an expect covering a decoder with a name, which is the shape that breaks.
The inline `|cols| |stmt| ...` form kept compiling and hid the regression.
CI pinned nightly-2026-08-23-fb208ba, which is older than roc-lang/roc#10984.
The inference change that broke the SQLite row decoders does not show up at that
pin, so neither the bug nor the test guarding it means anything until CI moves
past it.

08-30 is the newest published nightly. The platform uses no `List.sort`, so it
is clear of the sort segfault that nightly also carries (roc-lang/roc#10993,
fixed upstream by #11002 after 08-30 was cut).
@lukewilliamboswell
lukewilliamboswell merged commit efc1e95 into roc-lang:main Aug 31, 2026
24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants