Skip to content

A name declared twice in one scope transpiles at exit 0 and gcc rejects the C: two locals in one block, or a body local named like a parameter #2011

Description

@jlaustill

Bug

A name declared twice in one scope gets no diagnostic. The transpiler exits 0 and emits both declarations, and gcc rejects the C. Two shapes:

  1. Two locals of one name in the same block.
  2. A local in a function's outermost block with the name of one of the function's parameters. C gives the parameters and the function's outermost block one scope, so this is a redeclaration too.

ADR-057 allows shadowing "matching C behavior". C forbids a second declaration of a name in the same scope, so neither shape is shadowing.

Found while reviewing PR #1993. That PR changes how the second shape fails (below).

Reproduction (--target host)

u32 total <- 7;

u32 dup() {
    u32 a <- 1;
    u32 a <- 2;
    return a;
}

u32 plain(u32 a) {
    u32 a <- 2;
    return a;
}

u32 shadowing(u32 total) {
    u32 total <- 3;
    return total;
}

On main @ 7d7f27158, exit 0:

uint32_t dup(void) {
    uint32_t a = 1U;
    uint32_t a = 2U;          // gcc: redefinition of 'a'
    ...
uint32_t plain(uint32_t a) {
    uint32_t a = 2U;          // gcc: 'a' redeclared as different kind of symbol
    ...
uint32_t shadowing(uint32_t total) {
    uint32_t shadowing__total = 3U;   // compiles: only the local moved (ADR-057)
    return shadowing__total;
}

On PR #1993 @ 14d9241c9, exit 0. dup and plain are unchanged. shadowing now fails too, because the parameter moves as well (#1290) and the two declarations share one C name:

uint32_t shadowing(uint32_t shadowing__total) {
    uint32_t shadowing__total = 3U;   // gcc: 'shadowing__total' redeclared as different kind of symbol

Control: the same local one block deeper (if (total > 0) { u32 total <- 3; … }) is shadowing. It transpiles on the PR to an inner uint32_t shadowing__total = 3U;, compiles under -Wall -Wextra -Werror, and global.total still reads the global.

Cause

1.4 frames every function-body declaration. Since #1290 that includes the parameters, in the function's frame. Nothing checks a new declaration against the declarations already in its scope, so LexicalFrames.emittedName names both and render emits both.

Definition of done

  • A second declaration of a name in one scope is a diagnostic: two locals in the same block, and a local in a function's outermost block named like one of its parameters. One check decides both.
  • test-error fixtures for both shapes, including the shadowing-a-global variant of the parameter shape. Beside them goes a control that must stay silent: the same local one block deeper. Each is mutation-checked.

Activity

  1. added
    bugSomething isn't working
    code-generatorIssues in code generation/transpilation
    scopeScope and namespace features
    validation-bugMissing compile-time validation
    on Oct 11, 2026
  2. added 2 commits that reference this issue on Oct 11, 2026
  3. devin-ai-integration commented on Oct 11, 2026

    @devin-ai-integration
    Contributor

    Both boxes ticked, evidence from #1993 (head 78424449e, CI green: All Checks Passed, SonarCloud 0 open issues).

    • Box 1: RedeclarationAnalyzer (src/TRANSPILE/1-Analyze/RedeclarationAnalyzer.ts, commit 1d2c7cb) is the one check. It walks 1.4's lexical frames. A function's parameters and its outermost block are one scope, and every other block is its own. A repeat in one scope is E0425 '<name>' is already declared in this scope.

    • Box 2: tests/bugs/issue-2011-redeclaration-in-one-scope/:

      • two-locals-one-block (test-error)
      • local-redeclares-parameter (test-error)
      • local-redeclares-shadowing-parameter (test-error, test-adr: 057; the parameter shadows the global total)
      • control nested-block-shadows-parameter: the same local one block deeper; it executes in C and C++

      Mutations:

      • Not merging the parameters with the outermost block: the 2 parameter fixtures fail.
      • Not reporting a repeat: all 3 error fixtures fail.
      • The control passes under both.

    Written by Devin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcode-generatorIssues in code generation/transpilationscopeScope and namespace featuresvalidation-bugMissing compile-time validation

    Projects

    • Status
      Done

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions