mirror of
https://github.com/imjasonh/nescript
synced 2026-07-08 00:45:38 +00:00
analyzer: reject functions with more than 4 parameters (E0506)
The v0.1 calling convention passes parameters through four fixed zero-page slots ($04-$07). Functions declared with 5+ parameters were silently dropped past the 4th, producing a runtime miscompile with no compile-time signal — a trap I hit while building the War example (arm_fly took 6 params and silently corrupted fly_card and fly_face_up). Add E0506 to the analyzer so the over-arity case becomes a clear compile-time error pointing at the user's `fun` declaration with guidance toward globals or splitting. New tests cover both the 5- param rejection and the 4-param accept boundary. Documented in examples/war/COMPILER_BUGS.md §1, language-guide.md "Restrictions" section, and the error code table. https://claude.ai/code/session_0143dTgh3UeRrtfHgQwzcv5z
This commit is contained in:
parent
8ababdcec4
commit
155a0e7096
4 changed files with 69 additions and 0 deletions
|
|
@ -280,6 +280,7 @@ reset_score()
|
|||
|
||||
- **No recursion.** Both direct and indirect recursion are compile errors (`E0402`).
|
||||
- **Call depth limit.** The default maximum call depth is 8. Exceeding it produces error `E0401`.
|
||||
- **Maximum 4 parameters per function.** The v0.1 calling convention passes parameters via four fixed zero-page slots (`$04`-`$07`). Declaring a function with 5+ parameters produces error `E0506`. Pack additional state into globals or split the function into smaller helpers.
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -1203,6 +1204,7 @@ reference NEScript variables.
|
|||
| E0503 | Undefined function |
|
||||
| E0504 | Missing start declaration |
|
||||
| E0505 | Multiple start declarations|
|
||||
| E0506 | Function has too many parameters (max 4) |
|
||||
|
||||
### Warnings (W01xx)
|
||||
|
||||
|
|
|
|||
|
|
@ -1258,6 +1258,30 @@ impl Analyzer {
|
|||
));
|
||||
return;
|
||||
}
|
||||
// The v0.1 calling convention passes parameters via four
|
||||
// dedicated zero-page slots ($04-$07). Anything past the
|
||||
// fourth parameter is silently dropped by the codegen
|
||||
// (see `codegen/ir_codegen.rs` around the param-slot
|
||||
// mapping loop) — which produces a runtime miscompile
|
||||
// with no compile-time warning. Catch the over-arity case
|
||||
// here so users get a clear error instead.
|
||||
if fun.params.len() > 4 {
|
||||
self.diagnostics.push(
|
||||
Diagnostic::error(
|
||||
ErrorCode::E0506,
|
||||
format!(
|
||||
"function '{}' has {} parameters; the maximum is 4 in this version",
|
||||
fun.name,
|
||||
fun.params.len()
|
||||
),
|
||||
fun.span,
|
||||
)
|
||||
.with_help(
|
||||
"the v0.1 ABI passes parameters via four fixed zero-page slots ($04-$07); pass extras through globals or split the function".to_string(),
|
||||
),
|
||||
);
|
||||
return;
|
||||
}
|
||||
let sym_type = fun.return_type.clone().unwrap_or(NesType::U8);
|
||||
self.symbols.insert(
|
||||
fun.name.clone(),
|
||||
|
|
|
|||
|
|
@ -2059,3 +2059,44 @@ fn analyze_debug_frame_overrun_count_with_args_errors() {
|
|||
"expected E0203 for arg count mismatch, got: {errors:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn analyze_rejects_function_with_more_than_4_params() {
|
||||
// The v0.1 calling convention only allocates 4 zero-page
|
||||
// parameter slots ($04-$07). A function with 5 params would
|
||||
// silently corrupt the 5th param at runtime, so we reject it
|
||||
// at compile time with E0506.
|
||||
let errors = analyze_errors(
|
||||
r#"
|
||||
game "T" { mapper: NROM }
|
||||
fun too_many(a: u8, b: u8, c: u8, d: u8, e: u8) {
|
||||
a = 0
|
||||
}
|
||||
on frame { too_many(1, 2, 3, 4, 5) }
|
||||
start Main
|
||||
"#,
|
||||
);
|
||||
assert!(
|
||||
errors.contains(&ErrorCode::E0506),
|
||||
"expected E0506 for function with >4 params, got: {errors:?}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn analyze_accepts_function_with_exactly_4_params() {
|
||||
// 4 params is the maximum and should compile cleanly.
|
||||
analyze_ok(
|
||||
r#"
|
||||
game "T" { mapper: NROM }
|
||||
fun four_args(a: u8, b: u8, c: u8, d: u8) -> u8 {
|
||||
return a + b + c + d
|
||||
}
|
||||
var n: u8 = 0
|
||||
on frame {
|
||||
n = four_args(1, 2, 3, 4)
|
||||
wait_frame
|
||||
}
|
||||
start Main
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ pub enum ErrorCode {
|
|||
E0503, // undefined function
|
||||
E0504, // missing start declaration
|
||||
E0505, // multiple start declarations
|
||||
E0506, // function has too many parameters (max 4 in v0.1)
|
||||
|
||||
// W01xx: Warnings
|
||||
W0101, // expensive multiply/divide operation
|
||||
|
|
@ -62,6 +63,7 @@ impl fmt::Display for ErrorCode {
|
|||
Self::E0503 => "E0503",
|
||||
Self::E0504 => "E0504",
|
||||
Self::E0505 => "E0505",
|
||||
Self::E0506 => "E0506",
|
||||
Self::W0101 => "W0101",
|
||||
Self::W0102 => "W0102",
|
||||
Self::W0103 => "W0103",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue