@@ -574,3 +574,48 @@ gap is VM value-representation / intrinsic-dispatch cost (the next big lever).
574574- ** Tests / bench:** ` crate::selfhost_parity::lexer_perf_corpus `
575575 (` --release -- --ignored ` ).
576576- ** Status:** open (VM/stdlib lever identified; feeds perf roadmap).
577+
578+ ### SH-023 — self-hosted checker reaches RS0005 parity at declaration level; the merged callable namespace is the load-bearing rule
579+
580+ - ** Tool:** self-hosted checker (` selfhost/check.rss ` ) run on the reg-VM vs
581+ ` crate::analyze_source ` filtered to error-severity ` RS0005 `
582+ (DUPLICATE_DECLARATION), over the whole 546-file corpus.
583+ - ** Symptom (positive):** the checker reproduces RS0005 with ** 546/546** parity
584+ using ONLY top-level declaration structure — no statement/expression/pattern
585+ body parsing (confirms SH-021: RS0005 is decidable from declaration shape). It
586+ reuses the proven ` selfhost/parser.rss ` recognizer verbatim; the sole addition
587+ is carrying identifier TEXT on each token so names can be compared (the parser
588+ only kept a keyword/word id, which is 0 for all user identifiers).
589+ - ** Namespace grouping replicated (the interesting part — truth per
590+ ` crate::hir::lower::collect_item_signatures ` ):** duplicates are detected across
591+ exactly three groups —
592+ 1 . ** callable namespace = fn names + type CONSTRUCTOR names.** Every
593+ ` struct ` /` resource ` /` class ` /` opaque ` type registers BOTH a type-namespace
594+ entry AND a constructor entry into the SAME map that free functions use, so
595+ ` fn Foo ` collides with ` struct Foo ` (not "separate namespaces"). In the
596+ corpus this only matters via ` fn ` -vs-` fn ` (fixture ` duplicate-declarations ` ),
597+ but the faithful rule is the merge.
598+ 2 . ** type namespace = type names + sum names.** Sums register a type entry only
599+ (no constructor, so sums never collide with functions), and sum variant
600+ fields are NOT field-checked.
601+ 3 . ** per-type field names** for ` struct ` /` resource ` /` class ` /` opaque ` only
602+ (fixture ` duplicate-fields ` ). Implemented as: the token immediately before
603+ each ` : ` that sits at body-top-level (paren/bracket/angle/brace depth 0),
604+ which cleanly skips ` drop { ... } ` bodies, fn-typed field params, and
605+ generic type args.
606+ - ** Backend:** vm (checker is intrinsic/collection-bound like the lexer, cf.
607+ SH-022; not native-eligible).
608+ - ** Root cause / gaps:** none new. All prior constraints held without surprise —
609+ no char literals (SH-016), single-line boolean chains (SH-017), positionally
610+ threaded cursors returned by value (SH-018). ` Set<String> ` (` Set<String>.new() ` ,
611+ ` Set.contains<String> ` , ` Set.insert<String> ` ) worked as the duplicate detector;
612+ ` features: local ` was NOT needed (no ` StringBuilder ` /` local ` bindings). The
613+ scanner conservatively STOPS on the first malformed/unknown top-level item
614+ (mirroring the recognizer), which can only under-report on syntactically broken
615+ files — safe, since the analyzer emits RS0005 on exactly the 2 well-formed
616+ fixtures and the other 544 files stay CLEAN (zero false positives).
617+ - ** Classification:** docs (records the analyzer's duplicate-symbol namespace
618+ rule and that RS0005 is a declaration-only property).
619+ - ** Tests:** ` crate::selfhost_parity::checker_parity_tiny_sample ` and
620+ ` crate::selfhost_parity::checker_parity_corpus ` (` --ignored ` ).
621+ - ** Status:** done.
0 commit comments