Skip to content

fix: a generic is its file's; typedef chains, E3083 wording, one finding for a sibling's private name - #385

Merged
cmj0121 merged 7 commits into
mainfrom
fix/380-private-generics-by-file
Oct 21, 2026
Merged

cmj0121 merged 7 commits into
mainfrom
fix/380-private-generics-by-file

Conversation

@cmj0121

@cmj0121 cmj0121 commented Oct 21, 2026

Copy link
Copy Markdown
Owner

#380 and the three findings left unfiled from #381, filed as #382, #383 and #384.

Fixes

  • two files' private generics of one name collide (E3060) #380 a generic belongs to its file. When another file also declares the name, as a generic or a plain
    function, the template is keyed by its declaring file (least'k, the spelling c_ft_fresh already uses). Its
    instances mangle from that key, so two files' least$int become two C symbols. A bare call reaches its own
    file's template, then its own plain function, then the first declaring file's template, which E3001/E3114
    refuses. A qualified call reaches the template of the module it names. Two pub templates in one module are
    E4077, the flat collision a plain function already meets. E3060 and E3061 are now same-file only. This also
    fixes a silent wrong answer: strconv.to_string(42, 10) no longer reaches a local to_string[T].
  • a typedef over a typedef over a scalar is E9042 #382 type B = A over type A = u8 builds. The C typedef and the scalar conversion go to the chain's
    root, so B(300) wraps the same way A(300) does. E9042 is asked of the root. A cycle (type A = B,
    type B = A) is the new E3159 TypedefCycle, not a hang.
  • E3083's sentence says "safe code" twice and does not parse #383 E3083 now reads as one sentence: "… calling it from safe code is what the group is there to
    prevent".
  • a sibling file's private name called bare is both E3001 and E3114 #384 a sibling file's private fn, generic or type called bare gets one diagnostic (E5008), not E3001 plus
    E3114.

Tests

  • test-data (fix/380-private-generics-by-file): examples/1g/generics/main.out, examples/20_typedefs.out,
    codegen/typedef_over_a_typedef.zg.
  • examples/1g/generics/: two modules' pub least, private generics of one name in two files of one module,
    and a module's own plain least beside a sibling's generic. Each call answers its own.
  • refuse-check.sh: two-public-generics-of-one-module (E4077), typedef-chain-over-a-str (E9042),
    typedef-cycle (E3159). generic-declared-twice-across-modules is retired.
  • reject-check.sh: a private generic reached across modules or called bare (E3001), unsafe-group-fn-deferred
    (E3083), the sibling-private shapes marked one-finding, and a typedef over a typedef being its own type
    (E3038).

All CI gates were run locally and are green, with fixpoint at 7551348 bytes.

Not in this PR

  • Two files of one module, each with a private plain helper, are still E4077, while private generics in that
    position now build.
  • A private sibling constant called bare gives E3116 alone, with advice to write one.LIMIT, which privacy then
    refuses.
  • A one-file E3060/E3061 also reports E4073 (this predates the branch).

Closes #380
Closes #382
Closes #383
Closes #384

🤖 Generated with Claude Code

    A template was keyed by its bare name, so two files that each declared
    `fn least[T]` were one row and the second was refused as E3060, while
    two plain functions of one name in two files were two functions.

    - A free template another file also declares, as a generic or a plain
      function, is recorded under its file's key (`least'k`, files sorted
      for fixpoint). Its instances mangle from the key, so two files'
      `least` at `int` are two C symbols.
    - A bare call reaches its own file's template, else its own plain
      function, else the first declaring file's template, which then meets
      E3001 / E3114 as any other file's name does.
    - A qualified call reaches the template of the module it names; two
      `pub` ones in one module are E4077 at the call, as two plain
      functions are. `strconv.to_string` no longer reaches a local
      `to_string[T]`.
    - E3060 and E3061 are about one file. c_own_collided_tpl, the #379
      stopgap, is gone; findings speak the key back as the written name.
    The sentence wrote "{what} it from safe code" and every caller passed a
    whole clause, so it read "this call is in safe code it from safe code".
    Callers now pass the phrase the clause opens with: "calling it", and
    "calling it through a `spawn`" / "`defer`" on that route.
    A bare call to a private function of another file of the module said
    E3001 and then E3114, whose fix (spell it `one.least`) only meets E3001
    again. The spelling rules now speak about a public name only: E3114
    for a function, plain or generic, and E3115 beside E5008 for a type.
    E9042 asked whether a typedef's underlying type was a scalar, so
    `type Old = Age` over `type Age = int` was refused while the seed
    builds it. The scalar is now asked of the chain's root, followed in
    the parsed program, so a typedef may name one written below it.

    - The C typedef is emitted over the root, and a conversion into or
      out of a chained typedef goes through the root: `int(o)` was E4029,
      and `B2(300)` over `B = byte` silently printed 44.
    - A chain that goes round a cycle is E3159 at each declaration on it;
      it was a false E3035 "names no type". New code, its doc rows (en
      and zh-TW) and its refuse case.
    - A typedef written below the one that names it is no longer a false
      E3035.
    The submodule takes the expected output of examples/1g/generics (#380),
    a line for examples/20_typedefs, and a codegen case for #382.
…used once

    - examples/1g/generics: templates of one name in an entry, two modules
      and two files of one module each answer their own and link at `int`;
      a template body reads its own file; `strconv.to_string` is not a
      local `to_string[T]` (#380). examples/20_typedefs adds a typedef
      over a typedef (#382).
    - refuse: two private templates in two modules is no longer refused;
      two public ones in one module are E4077 at the qualified call; a
      typedef chain rooted in a `str` is E9042 at its own declaration.
    - reject: a bare call to another file's private template is E3001,
      also where two files declare one; a sibling's private function,
      generic or plain, and private type are refused once (`one-finding`,
      now also on the public E3114 / E3115 cases) (#384); E3083 pins its
      sentence for a call, a `spawn` and a `defer` (#383); an `Old` passed
      where its `Age` is wanted is E3038.

    Each case was red on the compiler before its fix.
…edef

    - package.md: the #380 deviation goes. A private template is its
      file's, in two modules or two files of one; two modules' public
      generics are reached through their qualifier; two `pub` ones in one
      module are E4077 at a qualified call. The sentence that two modules'
      public names collide is replaced by what does: a plain function two
      files of one module declare (E4077).
    - types.md: a typedef may be over a typedef, any depth and order; the
      root must be a scalar; a cycle is E3159 (#382).
    - diagnostics.md: E3060 and E3061 are about one file.
    - The seed README records that the seed does not return on a typedef
      cycle; examples/README indexes examples/1g/generics.
@cmj0121
cmj0121 merged commit b5909b3 into main Oct 21, 2026
15 of 16 checks passed
@cmj0121
cmj0121 deleted the fix/380-private-generics-by-file branch October 21, 2026 04:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant