← board

static functions in different crtl modules are treated as one unit

What

static at file scope means internal linkage: the same name in two translation units is two distinct functions, and C requires it to work. gcc, measured:

/* a.c */ static int helper(int x) { return x + 1;  }  int fa(void){ return helper(1); }
/* b.c */ static int helper(int x) { return x + 10; }  int fb(void){ return helper(1); }
gcc a.c b.c m.c  ->  "2 11"     — each calls ITS OWN helper

pxx pulls crtl's modules into one proc table with one CurrentUnitIdx, so the second definition looks like a redefinition of the first and the duplicate-definition warning fires on legal code.

Where it fires today

Five files in the tree, all false positives:

file name why it is legal
test/cisatty.c, test/cposix_io.c, crtl_lfs64_aliases_b234.c, crtl_posix_io_leaf_b238.c sysret static in BOTH lib/crtl/src/fcntl.c and lib/crtl/src/unistd.c
test/cvariadic_struct_b208.c (6x) __pxx_va_start_impl, __pxx_va_arg_gp/fp/cross, ...32 static in the HEADER lib/crtl/include/stdarg.h

Not currently a miscompile — checked

The two sysret bodies are byte-identical, so merging them changes nothing, and dup/open/close behave exactly as gcc does when tested individually. (An earlier reading of printf("%d %d", dup(fd) >= 0, close(fd)) looked like a failure; that was the TEST's bug — C leaves argument evaluation order unspecified, so close ran first.)

It becomes a miscompile the moment two crtl modules define a same-named static with different bodies, which nothing currently prevents. That is the real risk, and it is silent.

Blocks

bug-a-duplicate-definition-silently-accepted — its own "suggested gate" is to promote the warning to a hard error in both frontends, matching gcc and FPC. The Pascal side is clean tree-wide; the C side cannot be promoted while these five files warn on legal code.

Investigated 2026-08-05 — the concrete reason, and why it is not a one-liner

CPullCrtlForPrototypes' own header says it: the pulled crtl module is appended as #include lines to the MAIN token stream and "then goes through the same pass 1 / pass 2 as the main program." So fcntl.c and unistd.c are not two translation units that happen to share a unit index — in pxx's model they are one translation unit. CurrentUnitIdx is correct; the model is what differs from C.

That rules out the cheap fixes:

So the fix really is the structural one below: give each pulled .c its own unit identity. Recording this so the next attempt does not re-derive it.

Fix direction

Give each C source module its own unit identity so ProcUnitIdx distinguishes them (the warning's third term already tests it), and make a static definition private to its module rather than entered in a shared namespace. A static declared in a HEADER is per-including-TU by the same rule.