Introduce StaticType::Variadic for variadic parameters

This commit introduces `StaticType::Variadic` to represent variadic
parameter types in function signatures. This allows for more precise
type checking of functions that accept a variable number of arguments,
such as arithmetic operators.

Previously, variadic functions were handled using `StaticType::Any`,
which was too permissive and could lead to runtime errors. The new
`Variadic` type enforces that all arguments must conform to a specified
inner type.

Changes include:

- Adding `StaticType::Variadic` to `src/ast/types.rs`.
- Updating the type checker to handle `Variadic` types correctly when
  unifying with tuples or single arguments.
- Modifying built-in functions like arithmetic operators to use
  `Variadic` for their parameter types.
- Improving error messages for function call mismatches to be more
  specific.
- Adding a new test case to ensure arithmetic type mismatches are caught
  at compile time.
- A new example `data_stream_pipe_buffered.myc` is added.
- The `HMA.myc` example now has a `Skip: output` directive.
This commit is contained in:
2026-03-30 11:36:26 +02:00
parent f1621ed1ac
commit 8aa2a67b5b
9 changed files with 89 additions and 20 deletions
+3 -3
View File
@@ -34,7 +34,7 @@ fn test_call_argument_mismatch() {
assert!(
result
.unwrap_err()
.contains("Invalid arguments for function call")
.contains("no matching overload")
);
}
@@ -119,7 +119,7 @@ fn test_error_recovery_type_checker() {
assert!(
error_msgs
.iter()
.any(|m| m.contains("Invalid arguments for function call")),
.any(|m| m.contains("no matching overload")),
"Expected invalid arguments error"
);
assert!(result.ast.is_some(), "AST should be built with Error nodes");
@@ -157,6 +157,6 @@ fn test_error_recovery_multiple_errors() {
assert!(
error_msgs
.iter()
.any(|m| m.contains("Invalid arguments for function call"))
.any(|m| m.contains("no matching overload"))
);
}
+2 -2
View File
@@ -100,12 +100,12 @@ fn test_record_errors() {
// 1. Missing field
let res_missing = env.run_script("(.missing {:a 1})");
assert!(res_missing.is_err());
assert!(res_missing.unwrap_err().contains("Invalid arguments"));
assert!(res_missing.unwrap_err().contains("no matching overload"));
// 2. Not a record
let res_not_rec = env.run_script("(.name 123)");
assert!(res_not_rec.is_err());
assert!(res_not_rec.unwrap_err().contains("Invalid arguments"));
assert!(res_not_rec.unwrap_err().contains("no matching overload"));
}
#[test]
+14
View File
@@ -137,3 +137,17 @@ fn test_environments_from_shared_rtl_are_independent() {
assert_eq!(format!("{}", env1.run_script("(+ 1 2)").unwrap()), "3");
assert_eq!(format!("{}", env2.run_script("(+ 1 2)").unwrap()), "3");
}
/// Arithmetic operators with incompatible types (e.g. Float - Record)
/// must be caught at compile time, not silently return Void at runtime.
#[test]
fn test_arithmetic_type_mismatch_is_compile_error() {
let env = Environment::new();
// Float - Record must fail
let res = env.run_script("(do (def r {:price 42.0}) (- 10.0 r))");
assert!(
res.is_err(),
"Float - Record should be a compile error, not silently return Void"
);
}