fix(codegen): rename leading-underscore functions out of C's reserved namespace - #1490
Merged
Merged
Conversation
… namespace
Implements asks/leading-underscore-fn-collides-with-c-runtime.md.
A top-level Aether function was emitted as a C function with the same name
verbatim, so `_write(path, content)` became `void _write(const char*, const
char*)` — squarely inside the namespace C11 §7.1.3 reserves for the
implementation. MSVCRT/UCRT populate that namespace heavily (_write, _read,
_open, _close, _access, _aligned_malloc, ...), so on Windows the build failed:
error: conflicting types for '_write'; have 'void(const char*, const char*)'
note: previous declaration ... int(int, const void*, unsigned int)
Windows-only: glibc declares none of them, so the identical program built clean
on Linux. The diagnostic points at generated C rather than at the function
name, which is what makes it expensive to diagnose. Reported from the aeb line,
where one shared `_write` fixture broke 10 of 118 tests.
Took the ask's option 2 (rename) rather than option 1 (static). Option 1 is
insufficient on its own and the ask says so: a file-scope static whose name
matches a declared CRT prototype is STILL a conflicting-types error at compile
time. Rename closes the class, not the instance — `_read` and `_close` are
fixed by the same change, as is any name a future libc adds.
The mechanism is deliberately the one already in the file: #1366 renames a
function colliding with an extern to `ae_<name>` and rewrites its call sites.
This adds the same shape for a different trigger, so the two read as one
concept. `_write` becomes `ae_write` — still readable in a backtrace, and out
of the reserved namespace because the leading character is no longer `_`.
Scope is narrow on purpose: only a LEADING underscore, only top-level user
functions, skipping imported and @c_callback ones (whose symbols must stay
externally addressable verbatim). Runtime internals like `_aether_ctx_push` are
untouched — verified in the emitted C, where they and a genuine `_setmode` CRT
call remain as they were. The trailing-underscore file-local convention (#279)
is untouched and is what a caller should reach for instead.
Verified on winbaz (real MSYS2 MINGW64), the platform where every one of these
names previously refused to compile: the ask's 10-line repro now builds and
prints "leading underscore: a b", and the new regression test reports All PASS.
Tests: tests/regression/test_leading_underscore_fn.ae covers `_write`, `_read`
and `_close` (real CRT names), a leading-underscore name the CRT does NOT
declare (the rule is the namespace, not a blocklist), and the trailing-
underscore form, so the two conventions cannot silently drift apart.
Ask file recovered from b735c932, where it had been committed onto an unrelated
branch and so was absent from main.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements
asks/leading-underscore-fn-collides-with-c-runtime.md.The bug
A top-level Aether function was emitted as a C function with the same name verbatim, so
_write(path, content)becamevoid _write(const char*, const char*)— squarely inside the namespace C11 §7.1.3 reserves for the implementation. MSVCRT/UCRT populate it heavily (_write,_read,_open,_close,_access, …), so on Windows:Windows-only — glibc declares none of them, so the identical program built clean on Linux. And the diagnostic points at generated C rather than at the function name, which is what makes it expensive. On the aeb line one shared
_writefixture broke 10 of 118 tests.Option 2 (rename), not option 1 (static)
The ask offers three options and explains why the cheapest is insufficient — I agree and took the rename. A file-scope
staticwhose name matches a declared CRT prototype is still a conflicting-types error at compile time. Renaming closes the class:_readand_closeare fixed by the same change, as is any name a future libc adds.The mechanism is deliberately the one already in the file. #1366 renames a function colliding with an extern to
ae_<name>and rewrites its call sites; this adds the same shape for a different trigger, so the two read as one concept rather than two special cases._writebecomesae_write— still readable in a backtrace, and out of the reserved namespace because the leading character is no longer_.Scope, kept narrow on purpose
Only a leading underscore, only top-level user functions, skipping imported and
@c_callbackones (whose symbols must stay externally addressable verbatim).I checked the emitted C rather than assuming: runtime internals like
_aether_ctx_push,_aether_thunk_forceand a genuine_setmodeCRT call are untouched. Renaming those would break the runtime.The trailing-underscore file-local convention (#279) is untouched, and is what a caller should reach for instead.
Verified where the bug actually was
Rebuilt the compiler on winbaz (real MSYS2 MINGW64) and ran the ask's own 10-line repro:
Then the new regression test on the same box — All PASS, on the platform where every one of those names previously refused to compile.
Tests
tests/regression/test_leading_underscore_fn.aecovers_write,_readand_close(real CRT names), a leading-underscore name the CRT does not declare (the rule is the namespace, not a blocklist), and the trailing-underscore form — so #279 and this cannot silently drift apart.Note the test runs everywhere but only Windows could ever have failed to compile it. That is the point: a Linux-only assertion would not have caught the original bug, so what every platform checks is that the rename is transparent.
Verification
make ci— C suite 230/230;.ae978/980 with[PASS] regression_test_leading_underscore_fn, count up to 980. The two failures areintegration_http_server_h2(pre-existing, diagnosed inpesky_bug.md) andhttp_client_insecure_tls, which passes in isolation and is the known load-sensitive flake.gcc -Werror -Wall -Wextraon the changed codegen, locally before pushing.Also
The ask file was not on
main— it had been committed onto thefix/nightly-ffmpeg-depbranch (#1485) and so was absent here. Recovered fromb735c932and included.🤖 Generated with Claude Code