Proper errors for closure arity mismatch

This commit is contained in:
2026-05-21 10:05:48 +03:00
parent bf537cbeda
commit 2b02d382c2
2 changed files with 54 additions and 12 deletions
+37 -1
View File
@@ -1,4 +1,4 @@
use std::{fmt, io, rc::Rc};
use std::{fmt, io, ops::RangeInclusive, rc::Rc};
use crate::{
compile::CompileError,
@@ -37,6 +37,13 @@ pub enum ReadError {
Io(io::Error),
}
#[derive(Debug, PartialEq)]
pub struct ArgumentCountError {
pub function: Rc<BytecodeFunction>,
pub expected_range: RangeInclusive<usize>,
pub actual: usize,
}
#[derive(Debug, PartialEq, thiserror::Error)]
pub enum MachineError {
// VM itself
@@ -70,6 +77,8 @@ pub enum MachineError {
InvalidBranchTarget(usize, isize),
#[error("GET_TEMP with an empty temp register")]
TempRegisterEmpty,
#[error("{0}")]
ArgumentCount(ArgumentCountError),
// Syntax+evaluation
// #[error("evaluation error: {0}")]
@@ -131,3 +140,30 @@ impl PartialEq for ReadError {
}
}
}
impl fmt::Display for ArgumentCountError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let min_arity = *self.expected_range.start();
let max_arity = *self.expected_range.end();
let one_argument = min_arity == max_arity;
let too_few = self.actual < min_arity;
write!(
f,
"too {} arguments for function {}: expected ",
if too_few { "few" } else { "many" },
self.function
)?;
if one_argument {
write!(f, "{min_arity}")?;
write!(f, " argument")?;
if min_arity != 1 {
write!(f, "s")?;
}
} else {
write!(f, "{min_arity}-{max_arity} arguments")?;
}
write!(f, ", got {}", self.actual)
}
}
+17 -11
View File
@@ -1,6 +1,9 @@
use crate::{
compile::{CompileContext, CompileOptions},
error::{MachineError, MachineErrorAt, MachineErrorLocation, ValueConversionError},
error::{
ArgumentCountError, MachineError, MachineErrorAt, MachineErrorLocation,
ValueConversionError,
},
vm::{
Value,
env::Environment,
@@ -241,11 +244,12 @@ impl Machine {
}
};
if argument_count < closure.function.min_arity {
todo!("TODO function called with less arguments than expected")
}
if argument_count > closure.function.max_arity {
todo!("TODO function called with more arguments than expected")
if !(closure.function.min_arity..=closure.function.max_arity).contains(&argument_count) {
return Err(MachineError::ArgumentCount(ArgumentCountError {
function: closure.function.clone(),
expected_range: closure.function.min_arity..=closure.function.max_arity,
actual: argument_count,
}));
}
if closure.function.max_arity == usize::MAX {
todo!("VM support for &rest argument")
@@ -594,11 +598,13 @@ impl Machine {
args: &[Value],
) -> Result<Value, MachineErrorAt> {
let max_arity = closure.function.max_arity;
if args.len() < closure.function.min_arity {
todo!()
}
if args.len() > closure.function.max_arity {
todo!()
if !(closure.function.min_arity..=closure.function.max_arity).contains(&args.len()) {
return Err(MachineError::ArgumentCount(ArgumentCountError {
function: closure.function.clone(),
expected_range: closure.function.min_arity..=closure.function.max_arity,
actual: args.len(),
})
.at_unknown());
}
self.push(Value::Closure(closure))
.map_err(MachineErrorAt::at_unknown)?;