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
{{ message }}
Repository navigation
semantic: a nested def that redefines a name in the same function scope is refused with a misleading SPY0220 at the call (was a CS0128 ICE) — rebind, overload or a clean refusal is undecided #2314
Contract: a second def of an existing name in the same function scope has one decided meaning, and the compiler follows it on every route. The options are: rebind as Python does, overload by signature as a type-body def pair does (classes/overload_by_type), or refuse with a diagnostic that says "already defined". Today it has no decided meaning. The spec says nothing about it: no rebinding rule for def in docs/language_specification/ — grepped 2026-10-15.
Cells
axis values
program
observed
expected
lane
reach
found by
function scope × second def f with a different signature
@ 486c9a8: SPY0908 CS0128 (two C# local functions named F). @ cbabbf7: SPY0220 at f("a") — the second def now declares its own symbol (P54 #2250 Phase 2 task 3 stopped a nested def from writing the symbol it looked up), but the name still binds the first
python3 3.12 prints 2 then a! (rebinding); or a clean "already defined" refusal; or an overload — needs a ruling
false-refusal (was ice)
program
P54 Phase 2 task 3 direction probes (lane frozen)
Not measured: module-level and type-body twins with the SAME signature; a redefinition after a use inside a loop; the LSP route.
Acceptance
A ruling on rebind vs overload vs refusal, recorded on this issue.
Every cell in the table follows the ruling: no ICE, and no type error at a call site that only exists because the second def was ignored.
Cell: the CS0128 internal error is still reachable at HEAD when every call fits the FIRST def. Found by the spec-block gate on the P54 push (#2250, PR #2318).
The spec-block gate wraps a module-level overload example in main(), which turns it into this class's shape. Direction under sharpyc run:
overload_resolution.md:261 — same shape, returns str
SPY0908 CS0128
SPY0220 at f(3.14)
overload_resolution.md:320 — six pairs (f(b: uint8)/f(s: int16), q, g, r, h, p); every call type-checks against the first def
SPY0220 at p(LIMIT) (the second def p(s: str) had overwritten the first)
SPY0908 CS0128 ("a local function named 'F' is already defined")
So the class has two faces at HEAD:
a misleading SPY0220 when a call fits only a later def;
the CS0128 internal error when every call fits the first def.
The first two blocks are allowlisted against this issue (59ed0a8), and :320's old #1952 entry was deleted as stale. A ruling here (rebind, overload, or a clean refusal; C# refuses with CS0128) drains them. The spec examples are module-level overload sets, so they are correct as written: the cure is in the checker, or in the gate's wrapping. The spec text does not change.
def main() -> None: / def f(x: int) -> int: / return x + 1 / def f(x: str) -> str: / return x + "!" / print(f(1)) — a call that fits the FIRST def only
SPY0220 (exit 1)
SPY0908 CS0128 (exit 2)
the inverse of the flip the table above records (call-the-second went CS0128 → SPY0220 at cbabbf7); a refusal became an ICE — a regression in KIND inside this class, caused by the own-symbol rule (every nested def now declares its own symbol, so both F locals are emitted). python3 3.12 prints TypeError at the call (rebinding)
2
an overloaded def len pair inside an if in a def, with no call
SPY0908 CS0128
SPY0908 CS0128
pre-existing
Both cells belong to this issue's ruling (rebind / overload / refuse); a fix lane for #2250 did not patch them, per the verify round's no-widening rule.
Batching 2026-10-15 @ f8c37f9 — charter .claude/plans/remediation-round-2026-09-20-batching.md §3.20, P21e — declaration-position totality (order 6c′, directly after the chores band, before P69). Ruling R-HJ (owner, 2026-10-15): (a) refuse — a second def f in the SAME block is SPY0204 "function 'f' is already defined in this scope".
Internals (grep-only @ f8c37f9): C# local functions cannot be overloaded (CS0128); the spec has no rebinding rule for def and its only overload rule is for module and type bodies; SPY0204 DuplicateDefinition is the existing "already defined" family (R-CY reuses it for import m beside def m). python3 3.12 prints 2 then a! (rebinding). Mechanism: one validator arm at the declaration-position seam P21e builds; a def in a SIBLING block is a fresh binding (Python and C# agree); a def in a NESTED block shadowing an enclosing one follows B2 — the plan measures whether the emitter's name versioning covers local functions or Roslyn raises CS0136, in which case a Sharpy refusal pre-empts it. Guard: TypeDefinitionOwnSymbolMatrixTests (P54) gains the kind "redefined def" × block relation {same, sibling, nested} × signature {same, different} × call {fits first, fits second, none}; mutation (drop the arm → the same-block cells red: CS0128 or SPY0220). Direction record: CS0128 / SPY0220 → SPY0204; no running program is refused (every same-block pair is CS0128 at HEAD). The two spec_blocks_allowlist.txt rows citing this issue are correct module-level overload examples the gate wraps in main(): under this ruling they become a P20 gate-wrapping question (re-cite #1952), not a cell here. Rejected: (b) overload by signature as at module/type level (a materialized C# spelling for local functions; g = f ambiguous — one-way once relied on); (c) rebind as Python does (the first def dead code; the one mutable construct-owned binding, against B3/B4 — one-way). (a) is relaxable to (b) later. Lane / reach: false-refusal + ice · program. Class tracker:#2063 (this issue carries the ruling and its own cells). Pulled forward with the group by R-HK (owner: D2 a).
Closed by P21e (plan-f8f73c). Implementation 03eee7528..e4ed43321; verify round ..6c95019b8.
Verdict (R-HJ, measured branch): a nested def that re-declares a name already declared by a def of the same function in the same block, an enclosing block or an enclosed block is SPY0204 at the later def's name. Sibling blocks are fresh bindings: if/elif/else, try/except/else/finally, a loop body and its else, and, by the owner's ruling at the verify round (2026-10-23), the arms of a match. That ruling reverts the lead's one-scope ruling D5b in 8fc618b. D5b refused an is-chain-lowered match that ran at base, and Rust and Swift both scope match arms. When the match lowers to one C# switch, a def per arm is still SPY0908 CS0128, because local functions are not versioned. That is filed by class as #2361 and allowlisted there with the fingerprint required.
D6, recorded as the decided verdict: a call that fits only the refused second def keeps its SPY0220 cascade, just as a module-level duplicate does. Under refusal the second def is ignored by ruling, which supersedes this issue's pre-ruling acceptance bullet.
Probes under sharpyc run @ e4ed433 (base 03eee75 in parentheses):
i, def f(int) then def f(str) in one block: SPY0204 @4:9 (SPY0908 CS0128)
i2, the same signature twice: SPY0204 @4:9 (CS0128)
o, the call fits the second def: SPY0204 @4:9 + SPY0220 @6:13 (SPY0220 only)
j, sibling if/else blocks: runs, prints 2 / a! (unchanged)
Guard: the nested-def sub-matrix of TypeDefinitionOwnSymbolMatrixTests, 8 relations × 2 signatures × 3 calls = 48 cells. It has zero #2314 rows; its 6 #2361 rows require the CS0128 fingerprint. Mutations: def arm off → 24 cells red, and the nested_def_redefined_* fixtures fail with SPY0908; same-block-only → 12 red; siblings as one scope → 18 red (direction control); D5b restored → 12 red; restored → green.
Spec:function_definition.md § Nested functions (an SPY0204 error block and an executed sibling block). The two spec-block rows were re-cited to #1952 as gate-wrapping, with SPY0204 as their first error.
FINAL gate: whole solution @ 8fc618b: Core 4035/0/0, Stdlib 4294/0/0, Cli 1126/0/1, Compiler 50483/0/7, all measured. Lsp.Tests was red only in the 6 corpus-census pins (the removed D5b fixture stem) and is 22039/0/0 @ 6c95019 after the measured repin, with SHARPY_ROUTE_SWEEP_PINS_REQUIRED=1. Base gate 81988/0/8 @ e4ed433 (measured). Spec-block gate 1216/0 failing, allowlist issue-state, staleness and format checks are all clean. The commits are on dev (not yet pushed).
Activity
antonsynd commentedon Oct 15, 2026
Cell: the CS0128 internal error is still reachable at HEAD when every call fits the FIRST def. Found by the spec-block gate on the P54 push (#2250, PR #2318).
The spec-block gate wraps a module-level overload example in
main(), which turns it into this class's shape. Direction undersharpyc run:def f(x: int)/def f(x: float), thenf(42),f(3.14)f(3.14)strf(3.14)f(b: uint8)/f(s: int16),q,g,r,h,p); every call type-checks against the first defp(LIMIT)(the seconddef p(s: str)had overwritten the first)So the class has two faces at HEAD:
The first two blocks are allowlisted against this issue (59ed0a8), and :320's old #1952 entry was deleted as stale. A ruling here (rebind, overload, or a clean refusal; C# refuses with CS0128) drains them. The spec examples are module-level overload sets, so they are correct as written: the cure is in the checker, or in the gate's wrapping. The spec text does not change.
chore(spec-blocks): two wrapped overload examples are refused, not co…
antonsynd commentedon Oct 15, 2026
Two more cells @ 045ce6b (P54
/verify-implementationsibling-cell prober, axis "binding kind of a builtin/function name"):def main() -> None:/def f(x: int) -> int:/return x + 1/def f(x: str) -> str:/return x + "!"/print(f(1))— a call that fits the FIRST def onlyFlocals are emitted). python3 3.12 printsTypeErrorat the call (rebinding)def lenpair inside anifin a def, with no callBoth cells belong to this issue's ruling (rebind / overload / refuse); a fix lane for #2250 did not patch them, per the verify round's no-widening rule.
antonsynd commentedon Oct 15, 2026
Batching 2026-10-15 @ f8c37f9 — charter
.claude/plans/remediation-round-2026-09-20-batching.md§3.20, P21e — declaration-position totality (order 6c′, directly after the chores band, before P69). Ruling R-HJ (owner, 2026-10-15): (a) refuse — a seconddef fin the SAME block is SPY0204 "function 'f' is already defined in this scope".Internals (grep-only @ f8c37f9): C# local functions cannot be overloaded (CS0128); the spec has no rebinding rule for
defand its only overload rule is for module and type bodies; SPY0204DuplicateDefinitionis the existing "already defined" family (R-CY reuses it forimport mbesidedef m). python3 3.12 prints2thena!(rebinding). Mechanism: one validator arm at the declaration-position seam P21e builds; a def in a SIBLING block is a fresh binding (Python and C# agree); a def in a NESTED block shadowing an enclosing one follows B2 — the plan measures whether the emitter's name versioning covers local functions or Roslyn raises CS0136, in which case a Sharpy refusal pre-empts it. Guard:TypeDefinitionOwnSymbolMatrixTests(P54) gains the kind "redefined def" × block relation {same, sibling, nested} × signature {same, different} × call {fits first, fits second, none}; mutation (drop the arm → the same-block cells red: CS0128 or SPY0220). Direction record: CS0128 / SPY0220 → SPY0204; no running program is refused (every same-block pair is CS0128 at HEAD). The twospec_blocks_allowlist.txtrows citing this issue are correct module-level overload examples the gate wraps inmain(): under this ruling they become a P20 gate-wrapping question (re-cite #1952), not a cell here. Rejected: (b) overload by signature as at module/type level (a materialized C# spelling for local functions;g = fambiguous — one-way once relied on); (c) rebind as Python does (the first def dead code; the one mutable construct-owned binding, against B3/B4 — one-way). (a) is relaxable to (b) later. Lane / reach: false-refusal + ice · program. Class tracker: #2063 (this issue carries the ruling and its own cells). Pulled forward with the group by R-HK (owner: D2 a).test(compiler): the declaration-position matrix asserts the exact err…
docs(design): roster row for the declaration-position generator (refs #…
test(compiler): a declaration-position cell that fails to run has its…
antonsynd commentedon Oct 23, 2026
Closed by P21e (plan-f8f73c). Implementation
03eee7528..e4ed43321; verify round..6c95019b8.Verdict (R-HJ, measured branch): a nested
defthat re-declares a name already declared by adefof the same function in the same block, an enclosing block or an enclosed block is SPY0204 at the later def's name. Sibling blocks are fresh bindings:if/elif/else,try/except/else/finally, a loop body and itselse, and, by the owner's ruling at the verify round (2026-10-23), the arms of amatch. That ruling reverts the lead's one-scope ruling D5b in 8fc618b. D5b refused an is-chain-lowered match that ran at base, and Rust and Swift both scope match arms. When the match lowers to one C#switch, a def per arm is still SPY0908 CS0128, because local functions are not versioned. That is filed by class as #2361 and allowlisted there with the fingerprint required.D6, recorded as the decided verdict: a call that fits only the refused second def keeps its SPY0220 cascade, just as a module-level duplicate does. Under refusal the second def is ignored by ruling, which supersedes this issue's pre-ruling acceptance bullet.
Probes under
sharpyc run@ e4ed433 (base 03eee75 in parentheses):def f(int)thendef f(str)in one block: SPY0204 @4:9 (SPY0908 CS0128)if/elseblocks: runs, prints2/a!(unchanged)Guard: the nested-def sub-matrix of
TypeDefinitionOwnSymbolMatrixTests, 8 relations × 2 signatures × 3 calls = 48 cells. It has zero #2314 rows; its 6 #2361 rows require the CS0128 fingerprint. Mutations: def arm off → 24 cells red, and thenested_def_redefined_*fixtures fail with SPY0908; same-block-only → 12 red; siblings as one scope → 18 red (direction control); D5b restored → 12 red; restored → green.Spec:
function_definition.md§ Nested functions (an SPY0204 error block and an executed sibling block). The two spec-block rows were re-cited to #1952 as gate-wrapping, with SPY0204 as their first error.FINAL gate: whole solution @ 8fc618b: Core 4035/0/0, Stdlib 4294/0/0, Cli 1126/0/1, Compiler 50483/0/7, all measured. Lsp.Tests was red only in the 6 corpus-census pins (the removed D5b fixture stem) and is 22039/0/0 @ 6c95019 after the measured repin, with
SHARPY_ROUTE_SWEEP_PINS_REQUIRED=1. Base gate 81988/0/8 @ e4ed433 (measured). Spec-block gate 1216/0 failing, allowlist issue-state, staleness and format checks are all clean. The commits are ondev(not yet pushed).fix(validation): a builtin-named type alias is SPY0212 at every posit…
feat(validation): a nested def that re-declares a name of the same bl…
docs(spec): a repeated nested def in one function is an error, siblin…
fix(validation): defs of one name in two arms of one `match` are SPY0…
docs: DeclarationPositionValidator owns three verdicts — SPY0708, SPY…
fix(validation): the arms of one `match` are sibling blocks for neste…
test(lsp): the code-action and formatting-route census pins shrink by…
Merge dev into mainline: P21e declaration-position totality and its v…