feat: handle pipe fn infer TODOs

This improves error messages for `a |> b(x)`.

We need to do a special check when looping over the args
and unifying. This information is within a function that does not belong
to pipe typer so I used a closure to forward along a way to add
metadata to the error when the first argument in the loop has a
unification error. Simply adding the metadata at the pipe typer
level is not good enough because then we may annotate regular
unification errors from the args.
This commit is contained in:
rvcas 2023-10-03 01:01:46 -04:00 committed by Lucas
parent fb6cbbec8b
commit 135dbd8335
5 changed files with 129 additions and 27 deletions

View File

@ -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#"

View File

@ -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:

View File

@ -76,17 +76,21 @@ impl<'a, 'b> ExprTyper<'a, 'b> {
) -> Result<(TypedExpr, Vec<TypedCallArg>, Rc<Type>), 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<F>(
&mut self,
fun: TypedExpr,
mut args: Vec<CallArg<UntypedExpr>>,
location: Span,
) -> Result<(TypedExpr, Vec<TypedCallArg>, Rc<Type>), Error> {
map_err: F,
) -> Result<(TypedExpr, Vec<TypedCallArg>, Rc<Type>), 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,

View File

@ -202,9 +202,9 @@ impl<'a, 'b, 'c> PipeTyper<'a, 'b, 'c> {
args: Vec<CallArg<UntypedExpr>>,
location: Span,
) -> Result<TypedExpr, Error> {
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<TypedExpr, Error> {
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,

View File

@ -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"]