You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Two families of transpiler-invented temporaries are emitted with names a user is allowed to declare. When the names coincide, the generated temporary shadows the user's variable and every subsequent read silently binds to the wrong storage.
Both produce wrong runtime values with gcc -std=c99 -Wall -Wextra completely clean and the transpiler exiting 0. This is the same failure class as #1117 collision class 2 — the silent one — in a family ADR-063 did not cover.
uint8_t_msg_len=42U; /* the user's global */uint8_tcount(void) {
uint8_ttotal=0U;
size_t_msg_len=strlen(msg); /* shadows it */if (_msg_len>2) {
total=_msg_len+_msg_len; /* source said: msg.char_count + _msg_len */
}
returntotal;
}
Look at the emitted expression: total = _msg_len + _msg_len. The source was msg.char_count + _msg_len, i.e. 5 + 42 = 47. Both operands now resolve to the shadowing local.
$ gcc -std=c99 -Wall -Wextra -o lensilent lensilent.c # no diagnostics at all
$ ./lensilent;echo$?10 # expected 47
Generated — the MISRA 21.15 slice unroll introduces _tmp0 inside the function:
uint8_t_tmp0=7U; /* the user's global */voidstore(uint32_tmagic) {
constuint32_t_tmp0= (uint32_t)(magic); /* shadows it */buffer[0] = (uint8_t)(_tmp0);
...
buffer[0] =buffer[0U] +_tmp0; /* adds magic, not 7 */
}
$ gcc -std=c99 -Wall -Wextra -o tmpsilent tmpsilent.c # no diagnostics at all
$ ./tmpsilent;echo$?0 # expected 7
Both verified on main @ 1733ba70.
Why nothing catches it
-Wall -Wextra do not include -Wshadow. With -Wshadow gcc does report "declaration of '_msg_len' shadows a global declaration" — but that flag is not used by this project's pipeline and is not on by default anywhere.
E0201 does not apply: _msg_len and _tmp0 contain no __ and no trailing _, so they are perfectly legal C-Next identifiers. A leading underscore is explicitly legal under ADR-063.
scripts/batch-validate.mjs only ever runs over this repo's own generated tests/ output, never over a user's project, so it could not catch a user-side collision regardless.
Proposed fix — make it unrepresentable, not diagnosed
Both names are purely invented — no user-visible contract depends on their spelling. So they can simply be moved into a shape E0201 already forbids in user source:
Because a user identifier may not contain __, cnx__len__msg and cnx__tmp0 are unrepresentable in C-Next source. That closes the family structurally, with no new diagnostic and no new rule — reusing the guarantee ADR-063 already bought. It is strictly better than reserving a prefix, which would only turn a silent miscompile into an error message.
The same treatment applies to _cnx_tmp_<N> (ArgumentGenerator.ts:118), which has the same shape and is not covered by a cnx_ prefix rule anyway because it starts with an underscore.
Note this requires regenerating snapshots — _tmp0 … _tmp5 and _<var>_len appear in committed expected output.
The cnx_ prefix reservation issue — these two families are listed there as gaps the prefix rule does not close, because a prefix ban would produce a diagnostic where this fix produces impossibility.
Acceptance
_<var>_len and _tmp<N> (and _cnx_tmp_<N>) are renamed into a __-containing shape.
Both reproductions above become impossible to express rather than merely rejected.
Execution regression tests for both, asserting the runtime values (47 and 7), not just that they compile — a compile-only test would pass today.
Summary
Two families of transpiler-invented temporaries are emitted with names a user is allowed to declare. When the names coincide, the generated temporary shadows the user's variable and every subsequent read silently binds to the wrong storage.
Both produce wrong runtime values with
gcc -std=c99 -Wall -Wextracompletely clean and the transpiler exiting 0. This is the same failure class as #1117 collision class 2 — the silent one — in a family ADR-063 did not cover._<var>_len(strlen cache)CodeGenerator.ts:1293—`_${varName}_len`_tmp<N>(slice-assignment unroll)CodeGenState.ts:1247—`_tmp${this.tempVarCounter++}`Reproduction 1 —
_<var>_lenGenerated:
Look at the emitted expression:
total = _msg_len + _msg_len. The source wasmsg.char_count + _msg_len, i.e.5 + 42 = 47. Both operands now resolve to the shadowing local.Reproduction 2 —
_tmp<N>Generated — the MISRA 21.15 slice unroll introduces
_tmp0inside the function:Both verified on
main@1733ba70.Why nothing catches it
-Wall -Wextrado not include-Wshadow. With-Wshadowgcc does report "declaration of '_msg_len' shadows a global declaration" — but that flag is not used by this project's pipeline and is not on by default anywhere._msg_lenand_tmp0contain no__and no trailing_, so they are perfectly legal C-Next identifiers. A leading underscore is explicitly legal under ADR-063.scripts/batch-validate.mjsonly ever runs over this repo's own generatedtests/output, never over a user's project, so it could not catch a user-side collision regardless.Proposed fix — make it unrepresentable, not diagnosed
Both names are purely invented — no user-visible contract depends on their spelling. So they can simply be moved into a shape E0201 already forbids in user source:
Because a user identifier may not contain
__,cnx__len__msgandcnx__tmp0are unrepresentable in C-Next source. That closes the family structurally, with no new diagnostic and no new rule — reusing the guarantee ADR-063 already bought. It is strictly better than reserving a prefix, which would only turn a silent miscompile into an error message.The same treatment applies to
_cnx_tmp_<N>(ArgumentGenerator.ts:118), which has the same shape and is not covered by acnx_prefix rule anyway because it starts with an underscore.Note this requires regenerating snapshots —
_tmp0…_tmp5and_<var>_lenappear in committed expected output.Related
cnx_prefix reservation issue — these two families are listed there as gaps the prefix rule does not close, because a prefix ban would produce a diagnostic where this fix produces impossibility.Acceptance
_<var>_lenand_tmp<N>(and_cnx_tmp_<N>) are renamed into a__-containing shape.