diff --git a/crates/aiken-lang/src/tests/check.rs b/crates/aiken-lang/src/tests/check.rs index 35825c39..4b9b8431 100644 --- a/crates/aiken-lang/src/tests/check.rs +++ b/crates/aiken-lang/src/tests/check.rs @@ -3,7 +3,7 @@ use crate::{ builtins, expr::TypedExpr, parser, - tipo::error::{Error, Warning}, + tipo::error::{Error, UnifyErrorSituation, Warning}, IdGenerator, }; use std::collections::HashMap; @@ -1006,6 +1006,104 @@ fn trace_if_false_ko() { )) } +#[test] +fn pipe_with_wrong_type() { + let source_code = r#" + test foo() { + True |> bar + } + + fn bar(n: Int) { + n - 1 + } + "#; + + assert!(matches!( + check(parse(source_code)), + Err(( + _, + Error::CouldNotUnify { + situation: Some(UnifyErrorSituation::PipeTypeMismatch), + .. + } + )) + )) +} + +#[test] +fn pipe_with_wrong_type_and_args() { + let source_code = r#" + test foo() { + True |> bar(False) + } + + fn bar(n: Int, l: Bool) { + n - 1 + } + "#; + + assert!(matches!( + check(parse(source_code)), + Err(( + _, + Error::CouldNotUnify { + situation: Some(UnifyErrorSituation::PipeTypeMismatch), + .. + } + )) + )) +} + +#[test] +fn pipe_with_right_type_and_wrong_args() { + let source_code = r#" + test foo() { + 1 |> bar(1) + } + + fn bar(n: Int, l: Bool) { + n - 1 + } + "#; + + assert!(matches!( + check(parse(source_code)), + Err(( + _, + Error::CouldNotUnify { + situation: None, + .. + } + )) + )) +} + +#[test] +fn pipe_with_wrong_type_and_full_args() { + let source_code = r#" + test foo() { + True |> bar(False) + } + + fn bar(l: Bool) -> fn(Int) -> Int { + fn(n: Int) { + n - 1 + } + } + "#; + + assert!(matches!( + dbg!(check(parse(source_code))), + Err(( + _, + Error::CouldNotUnify { + situation: Some(UnifyErrorSituation::PipeTypeMismatch), + .. + } + )) + )) +} + #[test] fn utf8_hex_literal_warning() { let source_code = r#" diff --git a/crates/aiken-lang/src/tipo/error.rs b/crates/aiken-lang/src/tipo/error.rs index a99a07ef..396da272 100644 --- a/crates/aiken-lang/src/tipo/error.rs +++ b/crates/aiken-lang/src/tipo/error.rs @@ -1158,7 +1158,7 @@ fn suggest_unify( given }, Some(UnifyErrorSituation::PipeTypeMismatch) => formatdoc! { - r#"As I was looking at a pipeline you have defined, I realized that one of the pipe isn't valid. + r#"As I was looking at a pipeline you have defined, I realized that one of the pipes isn't valid. I am expecting the pipe to send into something of type: diff --git a/crates/aiken-lang/src/tipo/expr.rs b/crates/aiken-lang/src/tipo/expr.rs index 6030acb7..f765a398 100644 --- a/crates/aiken-lang/src/tipo/expr.rs +++ b/crates/aiken-lang/src/tipo/expr.rs @@ -76,17 +76,21 @@ impl<'a, 'b> ExprTyper<'a, 'b> { ) -> Result<(TypedExpr, Vec, Rc), Error> { let fun = self.infer(fun)?; - let (fun, args, typ) = self.do_infer_call_with_known_fun(fun, args, location)?; + let (fun, args, typ) = self.do_infer_call_with_known_fun(fun, args, location, |e| e)?; Ok((fun, args, typ)) } - pub fn do_infer_call_with_known_fun( + pub fn do_infer_call_with_known_fun( &mut self, fun: TypedExpr, mut args: Vec>, location: Span, - ) -> Result<(TypedExpr, Vec, Rc), Error> { + map_err: F, + ) -> Result<(TypedExpr, Vec, Rc), Error> + where + F: Copy + FnOnce(Error) -> Error, + { // Check to see if the function accepts labelled arguments match self.get_field_map(&fun, location)? { // The fun has a field map so labelled arguments may be present and need to be reordered. @@ -105,14 +109,20 @@ impl<'a, 'b> ExprTyper<'a, 'b> { let mut arguments = Vec::new(); - for (tipo, arg) in args_types.iter_mut().zip(args) { + for (index, (tipo, arg)) in args_types.iter_mut().zip(args).enumerate() { let CallArg { label, value, location, } = arg; - let value = self.infer_call_argument(value, tipo.clone())?; + let value = self.infer_call_argument(value, tipo.clone()); + + let value = if index == 0 { + value.map_err(map_err)? + } else { + value? + }; arguments.push(CallArg { label, diff --git a/crates/aiken-lang/src/tipo/pipe.rs b/crates/aiken-lang/src/tipo/pipe.rs index 2a564982..a1b552f5 100644 --- a/crates/aiken-lang/src/tipo/pipe.rs +++ b/crates/aiken-lang/src/tipo/pipe.rs @@ -202,9 +202,9 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> { args: Vec>, location: Span, ) -> Result { - let (function, args, tipo) = self - .expr_typer - .do_infer_call_with_known_fun(function, args, location)?; + let (function, args, tipo) = + self.expr_typer + .do_infer_call_with_known_fun(function, args, location, |e| e)?; let function = TypedExpr::Call { location, @@ -214,14 +214,11 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> { }; let args = vec![self.untyped_left_hand_value_variable_call_argument()]; - // TODO: use `.with_unify_error_situation(UnifyErrorSituation::PipeTypeMismatch)` - // This will require the typing of the arguments to be lifted up out of - // the function below. If it is not we don't know if the error comes - // from incorrect usage of the pipe or if it originates from the - // argument expressions. - let (function, args, tipo) = self - .expr_typer - .do_infer_call_with_known_fun(function, args, location)?; + let (function, args, tipo) = + self.expr_typer + .do_infer_call_with_known_fun(function, args, location, |e| { + e.with_unify_error_situation(UnifyErrorSituation::PipeTypeMismatch) + })?; Ok(TypedExpr::Call { location, @@ -239,14 +236,11 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> { location: Span, ) -> Result { arguments.insert(0, self.untyped_left_hand_value_variable_call_argument()); - // TODO: use `.with_unify_error_situation(UnifyErrorSituation::PipeTypeMismatch)` - // This will require the typing of the arguments to be lifted up out of - // the function below. If it is not we don't know if the error comes - // from incorrect usage of the pipe or if it originates from the - // argument expressions. - let (fun, args, tipo) = self - .expr_typer - .do_infer_call_with_known_fun(function, arguments, location)?; + let (fun, args, tipo) = + self.expr_typer + .do_infer_call_with_known_fun(function, arguments, location, |e| { + e.with_unify_error_situation(UnifyErrorSituation::PipeTypeMismatch) + })?; Ok(TypedExpr::Call { location, diff --git a/examples/hello_world/aiken.lock b/examples/hello_world/aiken.lock index a4353aa5..265756d4 100644 --- a/examples/hello_world/aiken.lock +++ b/examples/hello_world/aiken.lock @@ -13,4 +13,4 @@ requirements = [] source = "github" [etags] -"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1695230782, nanos_since_epoch = 772075000 }, "a5918f742d4589d2f5a91daf232eb03a2a0972a367ec0b016e9e8670e28c1b47"] +"aiken-lang/stdlib@main" = [{ secs_since_epoch = 1696308400, nanos_since_epoch = 613231000 }, "a721cf2738274f806efefb5a33c6ff9ae049476f0d45a42049b71793949f4d1d"]