fix: ci failing

This commit is contained in:
Gabriel Cruz 2026-07-15 15:20:34 -03:00
parent 265f38208f
commit 800d7f19ca
No known key found for this signature in database
GPG Key ID: 3C6977037D5A1EF5
7 changed files with 57 additions and 62 deletions

View File

@ -171,17 +171,18 @@ header shape from the library's ABI format. It carries two honest limits today:
An `abi = c` proc whose whole signature is scalar — fixed-width integer, float,
or bool params (a `string` return is fine, a `string` param is not) and no
structs, handles, or pointers — dispatches through a CBOR-free scalar fast path.
The `-d:targetLang=c_abi` generator emits real bindings for that shape: the
wrapper passes the scalar args inline (no request struct) and adapts the
raw-bytes reply into the same typed callback surface the flat-struct methods
use. The CBOR-speaking targets (`c`, `cpp`, `rust`, `cddl`) have no scalar
codegen, so under `-d:ffiGenBindings` they would omit such a proc from the
generated bindings — and `genBindings()` fails with an error naming the
affected procs. Resolve it by generating with `-d:targetLang=c_abi`, switching
the proc to `abi = cbor`, adding a non-scalar param so it takes the CBOR wire
shape, or passing `-d:ffiAllowScalarSkip` to accept the omission (the proc
still works over the scalar fast path; it's just absent from the generated
foreign bindings).
The `abi = c` C header (an `abi = c` library generated with `-d:targetLang=c`)
emits real bindings for that shape: the wrapper passes the scalar args inline
(no request struct) and adapts the raw-bytes reply into the same typed callback
surface the flat-struct methods use. The CBOR-speaking outputs — the CBOR C
header and the `cpp`, `rust`, `cddl` targets — have no scalar codegen, so under
`-d:ffiGenBindings` they would omit such a proc from the generated bindings —
and `genBindings()` fails with an error naming the affected procs. Resolve it by
making the whole library `abi = c` and generating C bindings, switching the proc
to `abi = cbor`, adding a non-scalar param so it takes the CBOR wire shape, or
passing `-d:ffiAllowScalarSkip` to accept the omission (the proc still works
over the scalar fast path; it's just absent from the generated foreign
bindings).
## Placement of `genBindings()`

View File

@ -1018,7 +1018,7 @@ func abiScalarRawFnName(libType: string): string =
## per-method trampoline converts into the typed reply.
return libType & "ScalarRawFn"
proc abiScalarArgParams(m: FFIProcMeta): seq[string] =
func abiScalarArgParams(m: FFIProcMeta): seq[string] =
## C parameters for a scalar method's args — passed inline by value, in both
## the raw export and the high-level wrapper (no Req struct).
var params: seq[string] = @[]

View File

@ -34,9 +34,9 @@ type
scalarFastPath*: bool
## True for an `abi = c` proc whose whole signature is scalar (see
## `isScalarOnly`): dispatches through the CBOR-free scalar fast path.
## Only the `c_abi` generator emits foreign bindings for it (inline
## scalar args + raw-bytes reply trampoline); the CBOR-speaking
## generators drop it (see `bindableProcs`).
## Only the `abi = c` C header emits a foreign binding for it (inline
## scalar args + raw-bytes reply trampoline); every other target — and
## the CBOR C header — drops it (see `bindableProcs`).
FFIFieldMeta* = object
name*: string
@ -138,5 +138,8 @@ const ffiOutputDir* {.strdefine.} = ""
# Nim src path override relative to outputDir (-d:ffiSrcPath); empty derives it.
const ffiSrcPath* {.strdefine.} = ""
# When true, scalar-only `abi = c` procs are silently omitted rather than failing the build. Off by default so the drop is loud; see genBindings().
# When set, scalar-only `abi = c` procs are silently omitted from bindings
# generated by targets without scalar codegen (every target except the
# `abi = c` C header) instead of failing the build. Off by default so the drop
# is loud; see genBindings().
const ffiAllowScalarSkip* {.booldefine.} = false

View File

@ -1519,9 +1519,10 @@ macro ffiEvent*(args: varargs[untyped]): untyped =
return generated
proc reportScalarFastPathDrops(procs: seq[FFIProcMeta]) {.compileTime.} =
## Only the `c_abi` generator emits foreign bindings for scalar-fast-path
## procs; every other target drops them. Fail loudly, naming them, unless
## `-d:ffiAllowScalarSkip` opts into the silent omission (then just hint).
## Only the `abi = c` C header emits foreign bindings for scalar-fast-path
## procs; every other target (and the CBOR C header) drops them. Fail loudly,
## naming them, unless `-d:ffiAllowScalarSkip` opts into the silent omission
## (then just hint).
var skipped: seq[string] = @[]
for p in procs:
if p.scalarFastPath:
@ -1537,11 +1538,12 @@ proc reportScalarFastPathDrops(procs: seq[FFIProcMeta]) {.compileTime.} =
return
error(
"genBindings: this target has no foreign-binding codegen for " &
"scalar-fast-path `abi = c` procs (only -d:targetLang=c_abi emits them), " &
"so these would be silently omitted from the generated bindings: " &
skipped.join(", ") & ".\n" & "Fix by one of:\n" &
" - generate with -d:targetLang=c_abi, or\n" &
" - switch the proc to `abi = cbor`, or\n" &
"scalar-fast-path `abi = c` procs, so these would be silently omitted " &
"from the generated bindings: " & skipped.join(", ") &
".\nThey are emitted only into the `abi = c` C header (an `abi = c` " &
"library generated with -d:targetLang=c).\n" & "Fix by one of:\n" &
" - make the library `abi = c` (declareLibrary(..., \"c\")) and generate " &
"C bindings, or\n" & " - switch the proc to `abi = cbor`, or\n" &
" - add a non-scalar param (e.g. a struct or handle) so it takes the " &
"CBOR wire shape, or\n" & " - pass -d:ffiAllowScalarSkip to accept the omission."
)
@ -1602,14 +1604,16 @@ macro genBindings*(
let lang = string_helpers.toLower(rawLang.strip())
if lang.len == 0:
continue
# `c_abi` is the one target with scalar-fast-path codegen, so it binds the
# full registry; the others drop scalar procs (loudly, unless skipped).
# The `abi = c` C header is the one output with scalar-fast-path codegen,
# so it binds the full registry. Every other target — and the CBOR C
# header — drops scalar procs (loudly, unless skipped).
let emitsScalars = lang == "c" and currentDefaultABIFormat == ABIFormat.C
let genProcs =
if lang == "c_abi":
if emitsScalars:
ffiProcRegistry
else:
bindableProcs(ffiProcRegistry)
if lang != "c_abi":
if not emitsScalars:
reportScalarFastPathDrops(ffiProcRegistry)
let outDir = bindingsOutputDir(lang, outputDir)
emitBindingsFor(

View File

@ -35,7 +35,7 @@ func bindableProcs*(procs: seq[FFIProcMeta]): seq[FFIProcMeta] =
## The procs the CBOR-speaking foreign-binding generators emit for.
## Scalar-fast-path procs are dropped: their C export takes inline scalar
## args, not the CBOR `(reqCbor, reqCborLen)` shape those backends assume, so
## emitting a CBOR caller for them would be wrong. Only the `c_abi` generator
## emitting a CBOR caller for them would be wrong. Only the `abi = c` C header
## has scalar codegen and binds the full registry (see genBindings()).
var kept: seq[FFIProcMeta] = @[]
for p in procs:

View File

@ -1,8 +1,8 @@
## Compile fixture for the scalar-fast-path genBindings() behavior (see
## tests/unit/test_scalar_skip_gen.nim). Under `-d:ffiGenBindings` only the
## `c_abi` target has foreign-binding codegen for the scalar `abi = c` proc
## below; any other target must fail — unless `-d:ffiAllowScalarSkip` is
## passed, which downgrades the drop to a hint.
## Compile fixture for the scalar-fast-path drop error (see
## tests/unit/test_scalar_skip_gen.nim). This is a CBOR-default library with one
## stray scalar `abi = c` proc, so no target can emit a binding for it: under
## `-d:ffiGenBindings` genBindings() must fail — unless `-d:ffiAllowScalarSkip`
## is passed, which downgrades the drop to a hint.
import ffi, chronos
@ -20,8 +20,8 @@ proc scalarskip_create*(cfg: SkipConfig): Future[Result[SkipLib, string]] {.ffiC
proc scalarskip_add*(
lib: SkipLib, a: int, b: int
): Future[Result[int, string]] {.ffi: "abi = c".} =
## All-scalar signature: dispatches through the CBOR-free fast path; only the
## `c_abi` target generates a foreign binding for it.
## All-scalar signature: dispatches through the CBOR-free fast path. In this
## CBOR-default library no target can emit a foreign binding for it.
return ok(lib.base + a + b)
genBindings()

View File

@ -1,12 +1,12 @@
## Drives the scalar-fast-path genBindings() behavior end to end: compiles
## `fixtures/scalar_skip_fixture.nim` (a library with an all-scalar `abi = c`
## proc) with `-d:ffiGenBindings` and asserts a CBOR-speaking target
## (`targetLang=c`) fails loudly, `-d:ffiAllowScalarSkip` downgrades the drop
## to a clean build, and `targetLang=c_abi` needs no skip at all — it emits a
## real binding for the scalar proc.
## Drives the scalar-fast-path drop error end to end: compiles
## `fixtures/scalar_skip_fixture.nim` (a CBOR-default library with a stray
## all-scalar `abi = c` proc) with `-d:ffiGenBindings` and asserts genBindings()
## fails loudly, and that `-d:ffiAllowScalarSkip` downgrades the drop to a clean
## build. The positive path — the `abi = c` C header emitting a real scalar
## binding — is covered by test_c_abi_codegen and the echo c_abi e2e.
##
## The fixture is compiled in a child `nim check` (search paths and compiler
## captured at compile time) so an expected failure is observed as a test
## captured at compile time) so its expected failure is observed as a test
## assertion, not this file's own compile error.
import std/[os, osproc, strutils, compilesettings]
@ -17,15 +17,14 @@ const
nimExe = getCurrentCompilerExe()
ffiSearchPaths = querySettingSeq(searchPaths)
proc genFixture(
lang: string, extraDefs: seq[string], outDir: string
): tuple[output: string, exitCode: int] =
proc genFixture(extraDefs: seq[string]): tuple[output: string, exitCode: int] =
let outDir = getTempDir() / "ffi_scalar_skip_out"
let cacheDir = getTempDir() / "ffi_scalar_skip_cache"
createDir(outDir)
var cmd = quoteShell(nimExe) & " check --hints:off --warnings:off"
for p in ffiSearchPaths:
cmd.add(" --path:" & quoteShell(p))
cmd.add(" -d:ffiGenBindings -d:targetLang=" & lang)
cmd.add(" -d:ffiGenBindings -d:targetLang=c")
cmd.add(" -d:ffiOutputDir=" & quoteShell(outDir))
for d in extraDefs:
cmd.add(" " & d)
@ -34,26 +33,14 @@ proc genFixture(
execCmdEx(cmd)
suite "scalar-fast-path drop is loud under -d:ffiGenBindings":
test "a CBOR target errors and names the dropped scalar proc":
let (output, code) = genFixture("c", @[], getTempDir() / "ffi_scalar_skip_out_c")
test "genBindings errors and names the dropped scalar proc":
let (output, code) = genFixture(@[])
check code != 0
check output.contains("scalarskip_add")
check output.contains("scalar-fast-path")
check output.contains("targetLang=c_abi")
check output.contains("-d:ffiAllowScalarSkip")
test "-d:ffiAllowScalarSkip downgrades the drop to a clean build":
let (output, code) = genFixture(
"c", @["-d:ffiAllowScalarSkip"], getTempDir() / "ffi_scalar_skip_out_c_skip"
)
check code == 0
check not output.contains("Error")
test "targetLang=c_abi needs no skip: the scalar proc has real codegen":
# `nim check` runs genBindings() but VM file writes are skipped, so this
# asserts the clean build only; the emitted wrapper text is covered by
# test_c_abi_codegen and the checked-in echo c_abi bindings.
let (output, code) =
genFixture("c_abi", @[], getTempDir() / "ffi_scalar_skip_out_c_abi")
let (output, code) = genFixture(@["-d:ffiAllowScalarSkip"])
check code == 0
check not output.contains("Error")