Skip to content

Commit

Permalink
Don't allow functions to be called with more arguments than expected (#…
Browse files Browse the repository at this point in the history
…402)

This was originally enabled when making function calls more flexible.
Calling with fewer arguments than expected by a function has use cases,
but I can't think of a good reason to allow calling a function with
extra arguments.
  • Loading branch information
irh authored Jan 8, 2025
1 parent f782445 commit 40e9402
Show file tree
Hide file tree
Showing 4 changed files with 25 additions and 19 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ The Koto project adheres to

## [0.16.0] Unreleased

### Changed

#### Language

- Calls to functions with more arguments than expected by the function will now throw an error.

### Fixed

#### Core Library
Expand Down
2 changes: 2 additions & 0 deletions crates/runtime/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ pub enum ErrorKind {
expected: String,
unexpected: Vec<KValue>,
},
#[error("Too many arguments - expected {expected}, found {actual}")]
TooManyArguments { expected: u8, actual: u8 },
#[error("Unexpected type - expected: '{expected}', found: '{}'", unexpected.type_as_string())]
UnexpectedType {
expected: String,
Expand Down
27 changes: 17 additions & 10 deletions crates/runtime/src/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2764,16 +2764,23 @@ impl KotoVm {
f.arg_count
};

if f.variadic && call_info.arg_count >= expected_arg_count {
// The last defined arg is the start of the var_args,
// e.g. f = |x, y, z...|
// arg index 2 is the first vararg, and where the tuple will be placed
let arg_base = call_info.frame_base + 1;
let varargs_start = arg_base + expected_arg_count;
let varargs_count = call_info.arg_count - expected_arg_count;
let varargs = KValue::Tuple(self.register_slice(varargs_start, varargs_count).into());
self.set_register(varargs_start, varargs);
// self.truncate_registers(varargs_start + 1);
if f.variadic {
if call_info.arg_count >= expected_arg_count {
// The last defined arg is the start of the var_args,
// e.g. f = |x, y, z...|
// arg index 2 is the first vararg, and where the tuple will be placed
let arg_base = call_info.frame_base + 1;
let varargs_start = arg_base + expected_arg_count;
let varargs_count = call_info.arg_count - expected_arg_count;
let varargs =
KValue::Tuple(self.register_slice(varargs_start, varargs_count).into());
self.set_register(varargs_start, varargs);
}
} else if call_info.arg_count > expected_arg_count {
return runtime_error!(ErrorKind::TooManyArguments {
expected: expected_arg_count,
actual: call_info.arg_count
});
}

// self is in the frame base register, arguments start from register frame_base + 1
Expand Down
9 changes: 0 additions & 9 deletions crates/runtime/tests/vm_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1150,15 +1150,6 @@ foo 42
check_script_output(script, KValue::Null);
}

#[test]
fn call_with_extra_args() {
let script = "
foo = |a, b| a + b
foo 10, 20, 30, 40
";
check_script_output(script, 30);
}

#[test]
fn nested_call_without_parens() {
let script = "
Expand Down

0 comments on commit 40e9402

Please sign in to comment.