Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -496,8 +496,8 @@ fn reexports_still_type_check() {
"Bool",
"Int",
Span {
lo: 128,
hi: 140,
lo: 137,
hi: 139,
},
),
),
Expand Down
48 changes: 23 additions & 25 deletions source/compiler/qsc_frontend/src/typeck/infer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -308,69 +308,67 @@ impl TySource {
#[derive(Clone, Debug)]
pub(super) enum ArgTy {
/// A missing argument, indicating partial application.
Hole(Ty),
Hole(Ty, Span),
/// A given argument.
Given(Ty),
Given(Ty, Span),
/// A list of arguments. This corresponds literally to tuple syntax, not to any expression of a tuple type.
Tuple(Vec<ArgTy>),
Tuple(Vec<ArgTy>, Span),
}

impl ArgTy {
/// Applies a function `f` to each type in the argument type.
fn map(self, f: &mut impl FnMut(Ty) -> Ty) -> Self {
match self {
Self::Hole(ty) => Self::Hole(f(ty)),
Self::Given(ty) => Self::Given(f(ty)),
Self::Tuple(items) => Self::Tuple(items.into_iter().map(|i| i.map(f)).collect()),
Self::Hole(ty, span) => Self::Hole(f(ty), span),
Self::Given(ty, span) => Self::Given(f(ty), span),
Self::Tuple(items, span) => {
Self::Tuple(items.into_iter().map(|i| i.map(f)).collect(), span)
}
}
}

/// Applies the argument type to a parameter type, generating constraints and errors.
fn apply(&self, param: &Ty, span: Span) -> App {
fn apply(&self, param: &Ty) -> App {
match (self, param) {
// If `arg` is a hole, then it doesn't matter what the param is,
// because the hole can be anything.
// However, we do know that the type of Arg must be Eq to the type of Param, so we
// add that to the constraints.
// Preserve the hole.
(Self::Hole(arg), _) => App {
(Self::Hole(arg, arg_span), _) => App {
holes: vec![param.clone()],
constraints: vec![Constraint::Eq {
expected: param.clone(),
actual: arg.clone(),
span,
span: *arg_span,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing that might help me read this is if you renamed the span argument to this function (can't highlight the line since it's outside the diff) to reflect whose span it is. Now that we have two spans getting passed around, it'd be good to disambiguate.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I genuinely can't tell what that span is. I'm pretty sure it's the span of the whole call. I think it effectively means "here's the span where you should report an error if you find one".

}],
errors: Vec::new(),
},
// If `arg` is a hole, then it doesn't matter what the param is,
// because the hole can be anything.
// However, we do know that the type of Arg must be Eq to the type of Param, so we
// add that to the constraints.
(Self::Given(arg), _) => App {
(Self::Given(arg, arg_span), _) => App {

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think a Given is the same as a Hole but the comment and implementation seem to be the same as above?

holes: Vec::new(),
constraints: vec![Constraint::Eq {
expected: param.clone(),
actual: arg.clone(),
span,
span: *arg_span,
}],
errors: Vec::new(),
},
// if both the arg and the param are tuples, then we must check
// the types of each element in the tuple and generate iterative applications.
(Self::Tuple(args), Ty::Tuple(params)) => {
(Self::Tuple(args, tuple_span), Ty::Tuple(params)) => {
let mut errors = Vec::new();
if args.len() != params.len() {
errors.push(Error(ErrorKind::TyMismatch(
Ty::Tuple(params.clone()).display(),
self.to_ty().display(),
span,
*tuple_span,
)));
}

let mut holes = Vec::new();
let mut constraints = Vec::new();
for (arg, param) in args.iter().zip(params) {
let mut app = arg.apply(param, span);
let mut app = arg.apply(param);
constraints.append(&mut app.constraints);
errors.append(&mut app.errors);
if app.holes.len() > 1 {
Expand All @@ -387,31 +385,31 @@ impl ArgTy {
}
}

(Self::Tuple(_), Ty::Infer(_)) => App {
(Self::Tuple(_, tuple_span), Ty::Infer(_)) => App {
holes: Vec::new(),
constraints: vec![Constraint::Eq {
expected: param.clone(),
actual: self.to_ty(),
span,
span: *tuple_span,
}],
errors: Vec::new(),
},
(Self::Tuple(_), _) => App {
(Self::Tuple(_, tuple_span), _) => App {
holes: Vec::new(),
constraints: Vec::new(),
errors: vec![Error(ErrorKind::TyMismatch(
param.display(),
self.to_ty().display(),
span,
*tuple_span,
))],
},
}
}

pub(super) fn to_ty(&self) -> Ty {
match self {
ArgTy::Hole(ty) | ArgTy::Given(ty) => ty.clone(),
ArgTy::Tuple(items) => Ty::Tuple(items.iter().map(Self::to_ty).collect()),
ArgTy::Hole(ty, _) | ArgTy::Given(ty, _) => ty.clone(),
ArgTy::Tuple(items, _) => Ty::Tuple(items.iter().map(Self::to_ty).collect()),
}
}
}
Expand Down Expand Up @@ -1039,7 +1037,7 @@ fn check_call(callee: Ty, input: &ArgTy, output: Ty, span: Span) -> (Vec<Constra
// generate constraints for the arg ty that correspond to any class constraints specified in
// the parameters

let mut app = input.apply(&arrow.input.borrow(), span);
let mut app = input.apply(&arrow.input.borrow());
let expected = if app.holes.len() > 1 {
Ty::Arrow(Rc::new(Arrow {
kind: arrow.kind,
Expand Down
70 changes: 53 additions & 17 deletions source/compiler/qsc_frontend/src/typeck/rules.rs
Original file line number Diff line number Diff line change
Expand Up @@ -264,25 +264,51 @@ impl<'a> Context<'a> {
}
ExprKind::BinOp(op, lhs, rhs) => self.infer_binop(expr.span, *op, lhs, rhs),
ExprKind::Block(block) => self.infer_block(block),
ExprKind::Call(callee, input) => {
let callee = self.infer_expr(callee);
let input = if has_holes(input) {
self.infer_hole_tuple_arg(input)
ExprKind::Call(callee_expr, input_expr) => {
let callee = self.infer_expr(callee_expr);
let input_expr = &**input_expr;
let input = if has_holes(input_expr) {
self.infer_hole_tuple_arg(input_expr)
} else {
let ty = self.infer_expr(input);
let ty = self.infer_expr(input_expr);
// If the outermost element is a tuple, we must wrap it in an `ArgTy::Tuple`.
match ty {
if let Partial {
ty: Ty::Tuple(tys),
diverges,
} = ty
{
let spans: Vec<_> = if let ExprKind::Tuple(items) = input_expr.kind.as_ref()
{
items.iter().map(|item| item.span).collect()
} else if let ExprKind::Paren(inner) = input_expr.kind.as_ref() {
if let ExprKind::Tuple(items) = inner.kind.as_ref() {
items.iter().map(|item| item.span).collect()
} else {
panic!("unexpected syntax kind: {:?}", inner.kind)
}
} else {
panic!("unexpected syntax kind: {:?}", input_expr.kind)
};
Partial {
ty: Ty::Tuple(tys),
ty: ArgTy::Tuple(
tys.into_iter()
.zip(spans)
.map(|(ty, span)| ArgTy::Given(ty, span))
.collect(),
input_expr.span,
),
diverges,
} => Partial {
ty: ArgTy::Tuple(tys.into_iter().map(ArgTy::Given).collect()),
diverges,
},
_ => Partial {
ty: ArgTy::Given(ty.ty),
}
} else {
let arg_span = if let ExprKind::Paren(inner) = input_expr.kind.as_ref() {
inner.span
} else {
input_expr.span
};
Partial {
ty: ArgTy::Given(ty.ty, arg_span),
diverges: false,
},
}
}
};
let output_ty = if callee.ty == Ty::Err {
Expand Down Expand Up @@ -723,7 +749,7 @@ impl<'a> Context<'a> {
ExprKind::Hole => {
let ty = self.inferrer.fresh_ty(TySource::not_divergent(expr.span));
self.record(expr.id, ty.clone());
converge(ArgTy::Hole(ty))
converge(ArgTy::Hole(ty, expr.span))
}
ExprKind::Paren(inner) => {
let inner = self.infer_hole_tuple_arg(inner);
Expand All @@ -739,9 +765,19 @@ impl<'a> Context<'a> {
tys.push(item.ty);
}
self.record(expr.id, Ty::Tuple(tys.iter().map(ArgTy::to_ty).collect()));
self.diverge_if_map(ArgTy::Given, diverges, converge(ArgTy::Tuple(tys)))
let span = expr.span;
self.diverge_if_map(
// This seems like it could be the span of the (first?) divergent
// tuple item, but the value doesn't seem to justify the complexity
|ty| ArgTy::Given(ty, span),
diverges,
converge(ArgTy::Tuple(tys, span)),
)
}
_ => {
let span = expr.span;
self.infer_expr(expr).map(|ty| ArgTy::Given(ty, span))
}
_ => self.infer_expr(expr).map(ArgTy::Given),
}
}

Expand Down
Loading
Loading