Skip to content

Commit 8c23ce4

Browse files
olwangclaude
andcommitted
feat(selfhost): checker step-2 milestones 2e/2f — RS0028, RS0033
- RS0028 INVALID_SELF_PARAMETER: a `self` param that isn't the first parameter of a qualified (dotted-name) method. - RS0033 INTEGER_LITERAL_OUT_OF_RANGE: a whole-file scan for a decimal-integer literal token overflowing i64 (all-digit text, leading zeros stripped, 19-digit boundary compared digit-by-digit against i64::MAX; float/hex excluded). CHECKER_TARGET_CODES now 12 codes (RS0002/3/4/5/6/10/11/12/16/17/28/33); checker_parity_corpus byte-exact over 619 files, 0 mismatches, 0 run-failures. Remaining diagnostics need semantic infrastructure (RS0007/8/9 type-Copy/body analysis; RS0021/24 a symbol-table pass) — documented in the ledger. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 72ea9e3 commit 8c23ce4

3 files changed

Lines changed: 81 additions & 9 deletions

File tree

crates/rsscript/src/selfhost_parity.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -503,7 +503,7 @@ fn parser_parity_corpus() {
503503
// ---------------------------------------------------------------------------
504504

505505
/// Diagnostic codes the rss checker is expected to reproduce.
506-
const CHECKER_TARGET_CODES: &[&str] = &["RS0002","RS0003","RS0004","RS0005","RS0006","RS0010","RS0011","RS0012","RS0016","RS0017","RS0028"];
506+
const CHECKER_TARGET_CODES: &[&str] = &["RS0002","RS0003","RS0004","RS0005","RS0006","RS0010","RS0011","RS0012","RS0016","RS0017","RS0028","RS0033"];
507507

508508
fn is_target_code(code: &str) -> bool {
509509
CHECKER_TARGET_CODES.contains(&code)

docs/ledgers/rss-selfhost-ledger.md

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -998,13 +998,24 @@ gap is VM value-representation / intrinsic-dispatch cost (the next big lever).
998998
item boundary), which had hidden the effects clause of `#deprecated(...) fn …`
999999
(and would have mis-scanned RS0002/3/11 there too). `CHECKER_TARGET_CODES` =
10001000
**10 codes**; `checker_parity_corpus` byte-exact **619 files, 0 mismatches, 0
1001-
run-failures**. STILL DEFERRED from this family: **RS0007** (retains-param) has a
1002-
semantic `type_ref_is_copy` sub-condition (a retained Copy param → RS0007) that
1003-
isn't token-decidable. Other deferred: RS0008 (missing-effect, needs Copy/sum
1004-
table), RS0009 (invalid-pure, needs body analysis), RS0033 (int-range, needs
1005-
literal-value parsing), RS0028 (invalid-self, protocol/first-param rules). Next
1006-
genuinely semantic tier: RS0021 exhaustiveness / RS0024 unknown-type (need a
1007-
`Map<String,Def>` symbol-table pass).
1001+
run-failures**.
1002+
- **Diagnostics (step 2, milestones 2e/2f — DONE, 2026-07-02):** added **RS0028**
1003+
(INVALID_SELF_PARAMETER — a `self` param that isn't the first parameter of a
1004+
qualified/dotted-name method; mirrors check_params) and **RS0033**
1005+
(INTEGER_LITERAL_OUT_OF_RANGE — a whole-file scan for a decimal-integer literal
1006+
token whose value overflows i64, mirroring check_integer_literal_range: all-digit
1007+
text, leading zeros stripped, 19-digit boundary compared against i64::MAX digit-
1008+
by-digit; float/hex literals excluded since their text isn't all digits).
1009+
`CHECKER_TARGET_CODES` = **12 codes** (RS0002/3/4/5/6/10/11/12/16/17/28/33);
1010+
`checker_parity_corpus` byte-exact **619 files, 0 mismatches, 0 run-failures**.
1011+
- **STILL DEFERRED (need semantic infrastructure):** **RS0007** (retains-param) has
1012+
a semantic `type_ref_is_copy` sub-condition (retained Copy param → RS0007) not
1013+
token-decidable; **RS0008** (missing-effect) needs a Copy/sum-type table +
1014+
noescape/owned/surface-ref classification; **RS0009** (invalid-pure, 7 files)
1015+
needs body analysis (manage/native-call/with-resource) + type analysis. Next
1016+
genuinely semantic tier: **RS0021** exhaustiveness / **RS0024** unknown-type (need
1017+
a `Map<String,Def>` symbol-table pass — the first self-hosted check that isn't
1018+
decidable from local token structure).
10081019
- **Lexer spans (step 3):** added a `len` field to the shared `Tok`
10091020
(= consumed source span `j-i`, matching the Rust lexer's `index-start`) and made
10101021
`lexer.rss` emit the real `<line>:<col>:<len>` prefix. `lexer_parity_corpus` is

selfhost/check.rss

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,6 +405,62 @@ fn fn_has_share_param(toks: read List<Tok>, start: read Int) -> Bool {
405405
return hit
406406
}
407407

408+
// RS0033: a decimal integer literal that does not fit i64 (check_integer_literal_
409+
// range: all-ASCII-digit text whose value overflows). Non-decimal / float literals
410+
// (containing `.`, `e`, `x`, …) are left alone — their text isn't all digits.
411+
fn is_int_out_of_range(text: read String) -> Bool {
412+
let cs = String.chars(value: read text)
413+
let n = List.len(list: read cs)
414+
if n == 0 { return false }
415+
let mut allDigit = true
416+
let mut i = 0
417+
while i < n {
418+
let code = Char.to_code(value: read List.get(list: read cs, index: read i))
419+
if code < 48 || code > 57 { allDigit = false }
420+
i = i + 1
421+
}
422+
if allDigit == false { return false }
423+
// Strip leading zeros (Rust's i64 parse accepts them: `007` → 7).
424+
let mut start = 0
425+
while start < (n - 1) && Char.to_code(value: read List.get(list: read cs, index: read start)) == 48 {
426+
start = start + 1
427+
}
428+
let digits = n - start
429+
if digits > 19 { return true }
430+
if digits < 19 { return false }
431+
// Exactly 19 significant digits: compare against i64::MAX digit-by-digit.
432+
let ms = String.chars(value: read "9223372036854775807")
433+
let mut j = 0
434+
let mut result = false
435+
let mut decided = false
436+
while j < 19 && decided == false {
437+
let a = Char.to_code(value: read List.get(list: read cs, index: read (start + j)))
438+
let b = Char.to_code(value: read List.get(list: read ms, index: read j))
439+
if a > b {
440+
result = true
441+
decided = true
442+
} else if a < b {
443+
decided = true
444+
}
445+
j = j + 1
446+
}
447+
return result
448+
}
449+
450+
// Whole-file scan: any decimal integer literal token out of i64 range → RS0033.
451+
fn has_out_of_range_int(toks: read List<Tok>) -> Bool {
452+
let n = List.len(list: read toks)
453+
let mut i = 0
454+
let mut hit = false
455+
while i < n {
456+
if tk_kind(toks: read toks, i: read i) == TOK_NUMBER {
457+
if is_int_out_of_range(text: read tk_text(toks: read toks, i: read i)) { hit = true }
458+
}
459+
i = i + 1
460+
}
461+
return hit
462+
}
463+
408464
// RS0028: a parameter named `self` that is not the first parameter of a qualified
409465
// method (check_params: `self` && (!is_qualified_method || index != 0)). A method
410466
// is qualified iff its name is dotted (`Type.method`).
@@ -485,6 +541,8 @@ fn main() -> Unit {
485541
let mut unknownEffect = false
486542
let mut removedRuntime = false
487543
let mut invalidSelf = false
544+
// RS0033: any decimal integer literal in the file that overflows i64.
545+
let outOfRangeInt = has_out_of_range_int(toks: read toks)
488546
let mut i = 0
489547
let mut running = true
490548
while running {
@@ -668,6 +726,9 @@ fn main() -> Unit {
668726
if invalidSelf {
669727
Log.write(message: read "RS0028")
670728
}
729+
if outOfRangeInt {
730+
Log.write(message: read "RS0033")
731+
}
671732
if hasUnknownFeat {
672733
Log.write(message: read "RS0016")
673734
}
@@ -677,7 +738,7 @@ fn main() -> Unit {
677738
if headerCount >= 2 {
678739
Log.write(message: read "RS0006")
679740
}
680-
let anyDiag = found || hasUnknownFeat || hasDupFeat || headerCount >= 2 || missingReturn || untypedParam || hasProfile || shareParam || unknownEffect || removedRuntime || invalidSelf
741+
let anyDiag = found || hasUnknownFeat || hasDupFeat || headerCount >= 2 || missingReturn || untypedParam || hasProfile || shareParam || unknownEffect || removedRuntime || invalidSelf || outOfRangeInt
681742
if anyDiag == false {
682743
Log.write(message: read "CLEAN")
683744
}

0 commit comments

Comments
 (0)