Skip to content

Dev - #2373

Merged
antonsynd merged 4 commits into
mainlinefrom
dev
Oct 26, 2026
Merged

Dev#2373
antonsynd merged 4 commits into
mainlinefrom
dev

Conversation

@antonsynd

Copy link
Copy Markdown
Owner

No description provided.

antonsynd and others added 4 commits October 25, 2026 18:42
… their class @abstract and every name they read, so both blocks compile and leave the spec-block allowlist (call-31 band; refs #1952)

The two blocks predated #1307's rule that an @abstract member needs an @abstract class, and each
carried a second defect behind the first error the allowlist recorded:

- properties.md "Inheritance and Interfaces" (was properties.md::2cad2b350f, recorded SPY0203
  first): SPY0493 "@abstract property 'area' in non-abstract class 'Shape'" + SPY0203 "Type
  'Circle' has no member 'radius'". Now `@abstract class Shape`, and `Circle` declares `radius`
  with an `__init__`.
- events.md "Abstract Events" (was events.md::7e16c0b9bc, recorded SPY0202 first): SPY0493
  "@abstract event 'on_update' in non-abstract class 'BasePublisher'" + SPY0202 "Type
  'UpdateEventArgs' not found". Now `@abstract class BasePublisher`, and the block declares
  `UpdateEventArgs(EventArgs)` (the file's prelude imports `EventArgs`).

Guard (the spec-block sweep): the old texts fail as measured with the built sharpyc @ 6a0b507
(both diagnostics above, `emit diagnostics` as-is); the new texts compile as-is (no errors). The two
allowlist rows are deleted, so a regression fails the sweep as a non-allowlisted block.
`bash build_tools/check_spec_blocks.sh --jobs 8`: 1227 blocks, 0 failing, allowlisted 476 -> 474,
EXIT=0 (measured).

Not touched: events.md's two `ref[T]` parameter blocks (511f0d2735, f6007db8e3) - posted on #2268,
P59 decides the spelling; their rows stay on #1952.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpT1WWr9aKoBrYhJCpTWuR
…er's value parameter, never its return annotation — at all six readers; a setter annotated other than None is SPY0496 (call-31 band, R-JD; refs #2067 #2370)

Contract (R-JD): a set-only property is typed by its setter's VALUE parameter; a setter's return
annotation is the accessor's return — `-> None` or absent — on every host {class, struct,
interface, module}, and anything else is refused, never ignored.

Class: six parallel readers of "which annotation declares this property's type" disagreed on the
setter arm. CheckModuleProperty read the value parameter; ResolvePropertyTypes took the FIRST
PropertyDef of the name and, for a setter, its return annotation; CheckClassProperty's fallback did
the same; the emitter's single-accessor path (FunctionStylePropertyType) read ReturnType first; the
combined-accessor path preferred the getter but fell back to `first.ReturnType`; hover read
Type ?? ReturnType. Cure: ONE syntax fact, `StatementExtensions.DeclaredTypeAnnotation`
(auto -> Type, getter -> return annotation, set/init -> the value parameter's annotation) plus its
multi-accessor overload (getter/auto first, then a setter's value parameter, whatever the written
order), read by all six. The emitter still decides nothing: it maps the annotation the user wrote,
as before. The checker resolves a setter's return annotation in a return position
(ResolveWriteAccessorReturnAnnotation, also for body-less interface/abstract accessors), and
PropertyValidator rule 9 refuses a resolved type other than void.

Diagnostic: SPY0496 REUSED (AccessorParameterNotExpressible; comment + explanation widened to
"accessor signature"): its rule is "an accessor's signature is fixed by C#" — the parameter list
was its only face so far, a setter's return is the other. Not a new code: one rule, one
explanation, one fix-it family.

Direction record (measured: the new 36-cell matrix run against the unfixed compiler in a worktree
@ 67d1a8b — 20 red / 48 green — and the built sharpyc @ 6a0b507):
- set-only `-> None`, class / struct / interface, with a store: SPY0220 "Cannot assign type 'int32'
  to 'None'" -> runs; with no store (any host): SPY0908 CS0547 "property or indexer cannot have
  void type" -> runs; module: SPY0908 CS0547 + CS1503 -> runs.
- set-then-get pair with `-> None` on the setter (class, struct; interface behind #2067): SPY0260
  at the getter's return + SPY0220 at the store -> runs (a FALSE refusal of an ordinary program,
  found here; the get-then-set order and the module pair already ran).
- `-> int` on a setter: RAN with the annotation silently ignored (class, struct, module × 3
  shapes; interface set-only) -> SPY0496 at each annotated setter. Worked -> refused, inside R-JD's
  text ("anything else refused"); the charter's "nothing that runs changes" did not hold for these
  cells — the `-> None` cells are the ones that never ran.
- getter/setter type mismatch (getter `-> int`, setter `x: str`), class: the setter-first order
  typed the property `str`; both orders now type it `int` and refuse the `str` store (SPY0220).

Guard: NoneAnnotationPositionMatrixTests gains the closed-axis theory shape {set-only,
get-then-set, set-then-get} × host {class, struct, interface, module} × setter annotation {none,
`-> None`, `-> int`} = 36 cells (run + printed value, or exactly one SPY0496 per annotated setter at
its annotation, never SPY0908), the written-order pair, and a literal-anchored count (36).
The four interface × pair × {none, `-> None`} cells are #2067's open "abstract get+set pair on an
interface -> CS0102 + CS0229" cell (red with a BARE setter too, so not this arm): pinned by
fingerprint (`new KnownRedInterfacePair("#2067", "CS0102")`, visible to the issue-state gate) and
failing when P30 fixes them (drain on fix). HoverServiceTests gains class and module cells.

Mutations (each applied alone, filter NoneAnnotationPositionMatrixTests, restored by copy + touch,
restored run 68/0/0):
- M1 the setter's return annotation as the type (DeclaredTypeAnnotation's set arm skipped when
  ReturnType != null): 6 red — the `-> None` cells.
- M2 written order decides (the overload's OrderBy dropped): 1 red — order: set_then_get.
- M3 the module setter's return annotation never resolved: 3 red — module × `-> int`.
- M4 the emitter alone reads ReturnType first (checker fixed): 4 red — set-only `-> None` (CS0547).
- Hover: `Type ?? ReturnType` restored: both hover cells red ("(property) level: None"); restored 2/0.

Spec (properties_function_style.md; R-BG drained the file's 7 allowlist rows FIRST, all #1952):
§Accessor Parameter Lists states the setter's return face with a runnable output block (`-> None`
set-only property, prints 5) and adds the `-> int` case to the SPY0496 error block (now marked
`error SPY0496`). The other six: `error SPY0406` marker (intentional method/property clash);
`fragment` on the cross-module `os` usage snippet (needs os.spy as a second module); real example
defects fixed — the Temperature block declared the `_celsius` field its own prose forbids
(SPY0407), the mixed auto+custom snippet was written at module level with `self` (SPY0232; now a
class), AppConfig read instance fields through the type (SPY0290; now `@static`) and its usage ran at
module level (now `main`, output-asserted, R-FY), Counter's usage likewise (now output-asserted).
Counter's "ERROR: setter is private" line is SPY0908 CS0272 under `run` — a missing refusal of the
access class (posted on #2074); under the emit-only sweep it would pass silently, so it stays as a
one-line `fragment` beside the runnable block. Sweep: 1229 blocks, 0 failing, allowlisted 474 ->
467, output-asserted 9 -> 12, error-asserted 95 -> 97, EXIT=0 (measured).

Edit loop (measured on this tree): Compiler.Tests filter (property/accessor/observer classes, the
None-annotation matrix, DiagnosticExplanations + codes, DispatchSiteInventory, StaticStateConformance,
EmitterCarrierOnly, SymbolFactMirror, SinkPushingTotality, LayoutFactConsumerScan,
TypeDefinitionOwnSymbolMatrixTests, FileBasedIntegrationTests) 3879/0/0; Lsp.Tests Hover +
FrontEndParity 104/0/0; `dotnet format whitespace sharpy.sln --verify-no-changes` EXIT=0. No
spy-sourced stdlib setter carries a return annotation (no regen). The band rides P21g's gate.

Out of this arm, posted on #2067: a class body accepts a getter/setter TYPE mismatch with no store
(runs) where the module host refuses it (SPY0220 "accessor type 'str' does not match").

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpT1WWr9aKoBrYhJCpTWuR
…_function_style.md now names in its `error SPY0406` marker (follow-up to a78e3aa; refs #2287)

a78e3aa marked the method/property name-clash example `<!-- spec-sweep: error SPY0406 -->`, so the
file names SPY0406 and TypedGeneratorRosterTests.SpecCodes_EveryNamedCodeIsClaimedByARow went red:
"properties_function_style.md names SPY0406 and no roster row claims it" (measured on the tree after
a78e3aa). The row cites #2287 like the file's siblings SPY0405 / SPY0407 / SPY0496 (#2287 owns the
lane for a file no typed generator emits).

The census is its own falsifier: without the row it is red (the run above); with it the roster,
census, spec-code, JudgeSource and verdict-conformance classes are 373/0/1 (the skip is a pre-existing
Skip=). a78e3aa's edit-loop filter did not include the roster classes — the recurring pre-commit
filter gap; chore 3's runs include them.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpT1WWr9aKoBrYhJCpTWuR
…bs(Unknown) cascade by shape — a KnownRedCascade beside KnownRedRefusal tolerates only that cell's measured follow-on codes, and a deterministic fact per face fails when it drains (call-31 band, R-JE; refs #2075)

The red: ErrorCompletenessPropertyTests.UndefinedTypeAtParameter_IsRejected drew SPY0202 + SPY0220 on
~1 seed in 80 (the P21f gate @ 6a0b507, identical @ 17c3d39): `def compute(arg0: Nope_xyz)` /
`if abs(arg0):` — abs of an Unknown argument is typed by abs's FIRST overload (int8), which the
condition refuses ("has no falsy case"). The sentinel (P17 phase 1, R-AZ) is the cure; R-JE pins it.

Mechanism:
- ErrorInjector.KnownRedCascade(Issue, Tolerated[], Observed), carried on InjectionResult.KnownCascade.
  JudgeDiagnostics' error arm accepts the expected code alone, or the expected code plus codes from
  Tolerated — never any other code. A cascade pin on a KnownRed-refusal or warning-arm injection
  throws (it only rides an error-arm injection with an expected code). The record joins
  PropertyOracleConformanceTests' CodeSinks; `new KnownRedCascade("#2075", ...)` is read by the
  issue-state gate's KNOWNRED_CELL_RE.
- The lane attaches ErrorInjector.AbsOfUnknownCascade ONLY when a function of the mutated module
  passes a parameter annotated with the injected undefined type to `abs`; every other sample is
  judged exactly as before. RunInjectionTest reports "KnownRedCascade #2075 — k/n on the pinned shape".

Shape and faces, measured with the built sharpyc @ a78e3aa: over 17 builtins called on an
undefined-typed argument in an `if` and a `while` test, only `abs` cascades with SPY0220 (`sum` draws
SPY0354, a shape this lane never generates — posted on #2075). A 2000-sample run with the arm removed
went red on a SECOND face of the same cell: `arg0 = abs(arg0)` then `90 if False else arg0` — SPY0227
"no best common type" (int8 vs int). DEVIATION from R-JE's literal text ("tolerated: SPY0220"): the
pin tolerates both measured faces [SPY0220, SPY0227] of the one root, because pinning one face leaves
the lane seed-flaky on the other — the condition R-JE exists to end. Still keyed on the abs shape,
still refusing any unrelated code.

Guards (JudgeSourceTests, literal programs): each face's program, unpinned, still draws exactly
[SPY0202, face] (the drain signal: it fails the first time a face stops cascading); each is accepted
under the pin; a third unrelated code (SPY0200) is refused under the pin; a pin tolerating another code
(SPY0354) does not accept the cascade; the lane attaches the pin on the abs shape only (a comparison
and a `len(str(...))` base get none).

Mutations (filter JudgeSourceTests; restored by copy + touch; restored run 26/0/0):
- C1 the cascade arm dropped: AcceptsTheExpectedCodePlusAToleratedFace red (both faces).
- C2 any follow-on accepted: RefusesAThirdCode + ToleratesOnlyItsOwnCode red.
- C3 the SPY0227 face dropped from the pin: EachFaceStillCascades + Accepts...(SPY0227) red.
- C4 the pin attached on every shape: IsAttachedOnlyOnTheAbsOfTheMutatedParameterShape red (2 rows).
- C5 the drain simulated (the face-1 program no longer cascades): EachFaceStillCascades red.
Lane evidence, SHARPY_PROPERTY_ITER=2000 (20× the in-suite count), fresh seeds: with the pin green
three times (44/1695, 41/1700, 35/1662 samples on the pinned shape; the generic-function lane
2000/2000 with none on it); with the arm removed red, 3/1660 misjudged (the SPY0227 face first).

Edit loop: JudgeSource, both undefined-parameter lanes, PropertyOracleConformance, TypedGeneratorRoster
+ every *Roster*/*Census*/*SpecCode*/*SpecBlock* class: 375/0/1 (the skip is a pre-existing Skip=);
`dotnet format whitespace sharpy.sln --verify-no-changes` EXIT=0. Tests only; no compiler change.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MpT1WWr9aKoBrYhJCpTWuR
@antonsynd
antonsynd merged commit 217a8f2 into mainline Oct 26, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant