diff --git a/dust-lang/src/abstract_tree/built_in_function.rs b/dust-lang/src/abstract_tree/built_in_function.rs index d69b202..b8fc9e9 100644 --- a/dust-lang/src/abstract_tree/built_in_function.rs +++ b/dust-lang/src/abstract_tree/built_in_function.rs @@ -245,7 +245,7 @@ impl FunctionLogic for JsonParse { }; Type::Function { - type_parameters: None, + type_parameters: Some(vec![Identifier::from("T")]), value_parameters: Some(vec![(Identifier::from("input"), type_t.clone())]), return_type: Some(Box::new(type_t)), } diff --git a/dust-lang/src/abstract_tree/function_call.rs b/dust-lang/src/abstract_tree/function_call.rs index 5506b16..9fec472 100644 --- a/dust-lang/src/abstract_tree/function_call.rs +++ b/dust-lang/src/abstract_tree/function_call.rs @@ -65,19 +65,38 @@ impl AbstractNode for FunctionCall { return_type: _, } = function_node_type { - if let (Some(parameters), Some(arguments)) = (type_parameters, &self.type_arguments) { - if parameters.len() != arguments.len() { + match (type_parameters, &self.type_arguments) { + (Some(parameters), Some(arguments)) => { + if parameters.len() != arguments.len() { + return Err(ValidationError::WrongTypeArguments { + arguments: arguments.clone(), + parameters: parameters.clone(), + }); + } + + for (identifier, constructor) in parameters.into_iter().zip(arguments.iter()) { + let r#type = constructor.construct(context)?; + + context.set_type( + identifier, + r#type, + self.function_expression.position(), + )?; + } + } + (Some(parameters), None) => { return Err(ValidationError::WrongTypeArguments { - arguments: arguments.clone(), + arguments: Vec::with_capacity(0), parameters: parameters.clone(), }); } - - for (identifier, constructor) in parameters.into_iter().zip(arguments.iter()) { - let r#type = constructor.construct(context)?; - - context.set_type(identifier, r#type, self.function_expression.position())?; + (None, Some(arguments)) => { + return Err(ValidationError::WrongTypeArguments { + arguments: arguments.clone(), + parameters: Vec::with_capacity(0), + }); } + (None, None) => {} } match (value_parameters, &self.value_arguments) { diff --git a/dust-lang/src/abstract_tree/type.rs b/dust-lang/src/abstract_tree/type.rs index 00fea89..3855ad6 100644 --- a/dust-lang/src/abstract_tree/type.rs +++ b/dust-lang/src/abstract_tree/type.rs @@ -43,8 +43,20 @@ pub enum Type { } impl Type { + /// Returns a concrete type, either the type itself or the concrete type of a generic type. + pub fn concrete_type(&self) -> &Type { + match self { + Type::Generic { + concrete_type: Some(concrete_type), + .. + } => concrete_type.concrete_type(), + _ => self, + } + } + + /// Checks that the type is compatible with another type. pub fn check(&self, other: &Type) -> Result<(), TypeConflict> { - match (self, other) { + match (self.concrete_type(), other.concrete_type()) { (Type::Any, _) | (_, Type::Any) | (Type::Boolean, Type::Boolean) diff --git a/dust-lang/src/lib.rs b/dust-lang/src/lib.rs index 37d1e3d..4b964fb 100644 --- a/dust-lang/src/lib.rs +++ b/dust-lang/src/lib.rs @@ -124,6 +124,10 @@ pub struct InterpreterError { } impl InterpreterError { + pub fn new(source_id: Arc, errors: Vec) -> Self { + InterpreterError { source_id, errors } + } + pub fn errors(&self) -> &Vec { &self.errors } @@ -477,6 +481,8 @@ impl InterpreterError { #[cfg(test)] mod tests { + use abstract_tree::{AbstractNode, SourcePosition}; + use self::standard_library::std_full_compiled; use super::*; @@ -486,6 +492,9 @@ mod tests { let context = Context::new(); for abstract_tree in std_full_compiled() { + abstract_tree + .define_and_validate(&context, true, SourcePosition(0, usize::MAX)) + .unwrap(); abstract_tree.run(&context, true).unwrap(); } } diff --git a/dust-lang/tests/functions.rs b/dust-lang/tests/functions.rs index 0b357dd..8be61d1 100644 --- a/dust-lang/tests/functions.rs +++ b/dust-lang/tests/functions.rs @@ -1,4 +1,7 @@ +use abstract_tree::{Expression, ValueNode, WithPos}; use dust_lang::*; +use error::{DustError, ValidationError}; +use identifier::Identifier; #[test] fn function_scope() { @@ -109,3 +112,54 @@ fn recursion() { Ok(Some(Value::integer(13))) ); } + +#[test] +fn value_argument_error() { + assert_eq!( + interpret( + "test", + " + foobar = fn (a: int, b: int) -> int { a + b } + foobar(1) + " + ), + Err(InterpreterError::new( + "test".into(), + vec![DustError::Validation { + error: ValidationError::WrongValueArguments { + parameters: vec![ + (Identifier::new("a"), Type::Integer), + (Identifier::new("b"), Type::Integer), + ], + arguments: vec![Expression::Value( + ValueNode::Integer(1).with_position((78, 79)), + )], + }, + position: (71, 80).into() + }] + )) + ); +} + +#[test] +fn type_argument_error() { + assert_eq!( + interpret( + "test", + " + foobar = fn (a: T) -> T { a } + foobar(1) + " + ), + Err(InterpreterError::new( + "test".into(), + vec![DustError::Validation { + error: ValidationError::WrongTypeArguments { + parameters: vec![Identifier::new("T")], + arguments: vec![] + }, + position: (59, 68).into() + }] + )) + ); +}