Skip to content

Commit

Permalink
fix: Take implicits into account in type queries on infix expressions
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Mar 17, 2018
1 parent f2ae746 commit 8651be3
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 12 deletions.
12 changes: 2 additions & 10 deletions base/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -819,15 +819,7 @@ impl Typed for Expr<Symbol> {
| Expr::Tuple { ref typ, .. } => Ok(typ.clone()),
Expr::Literal(ref lit) => lit.try_type_of(env),
Expr::IfElse(_, ref arm, _) => arm.try_type_of(env),
Expr::Infix { ref op, .. } => op.value
.typ
.as_function()
.and_then(|(_, ret)| ret.as_function())
.map(|(_, ret)| ret.clone())
.ok_or_else(|| {
debug!("`{}` is not a binary function type", op.value.typ);
"Expected function type in binop".to_string()
}),
Expr::Infix { ref op, .. } => get_return_type(env, &op.value.typ, 2),
Expr::LetBindings(_, ref expr)
| Expr::TypeBindings(_, ref expr)
| Expr::Do(Do { body: ref expr, .. }) => expr.try_type_of(env),
Expand Down Expand Up @@ -878,7 +870,7 @@ fn get_return_type(
return Ok(alias_type.clone());
}

let alias_type = alias_type.remove_forall();
let alias_type = alias_type.remove_forall_and_implicit_args();
if let Some((_, ret)) = alias_type.as_function() {
return get_return_type(env, ret, arg_count - 1);
}
Expand Down
18 changes: 18 additions & 0 deletions base/src/types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,16 @@ where
})
}

pub fn function_implicit<I>(args: I, ret: T) -> T
where
I: IntoIterator<Item = T>,
I::IntoIter: DoubleEndedIterator<Item = T>,
{
args.into_iter().rev().fold(ret, |body, arg| {
T::from(Type::Function(ArgType::Implicit, arg, body))
})
}

pub fn generic(typ: Generic<Id>) -> T {
T::from(Type::Generic(typ))
}
Expand Down Expand Up @@ -1094,6 +1104,14 @@ impl<Id> ArcType<Id> {
remove_forall(self)
}

pub fn remove_forall_and_implicit_args(&self) -> &ArcType<Id> {
match **self {
Type::Function(ArgType::Implicit, _, ref typ) => typ.remove_forall_and_implicit_args(),
Type::Forall(_, ref typ, _) => typ.remove_forall_and_implicit_args(),
_ => self,
}
}

pub fn skolemize(&self, named_variables: &mut FnvMap<Id, ArcType<Id>>) -> ArcType<Id>
where
Id: Clone + Eq + Hash,
Expand Down
67 changes: 66 additions & 1 deletion base/tests/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ use std::ops::Deref;

use pretty::{Arena, DocAllocator};

use base::kind::Kind;
use base::ast::{Expr, Literal, SpannedExpr, Typed, TypedIdent};
use base::kind::{ArcKind, Kind, KindEnv};
use base::types::*;
use base::symbol::{Symbol, SymbolRef};
use base::source::Source;
use base::pos::{self, BytePos, Span, Spanned};

fn type_con<I, T>(s: I, args: Vec<T>) -> Type<I, T>
where
Expand Down Expand Up @@ -341,3 +344,65 @@ fn break_record() {
"#
);
}

pub struct MockEnv;

impl KindEnv for MockEnv {
fn find_kind(&self, _id: &SymbolRef) -> Option<ArcKind> {
None
}
}

impl TypeEnv for MockEnv {
fn find_type(&self, _id: &SymbolRef) -> Option<&ArcType> {
None
}

fn find_type_info(&self, _id: &SymbolRef) -> Option<&Alias<Symbol, ArcType>> {
None
}

fn find_record(
&self,
_fields: &[Symbol],
_selector: RecordSelector,
) -> Option<(ArcType, ArcType)> {
None
}
}

pub type SpExpr = SpannedExpr<Symbol>;

pub fn intern(s: &str) -> Symbol {
Symbol::from(s)
}

pub fn no_loc<T>(value: T) -> Spanned<T, BytePos> {
pos::spanned(Span::default(), value)
}

pub fn int(i: i64) -> SpExpr {
no_loc(Expr::Literal(Literal::Int(i)))
}

pub fn binop(l: SpExpr, s: &str, r: SpExpr) -> SpExpr {
no_loc(Expr::Infix {
lhs: Box::new(l),
op: no_loc(TypedIdent::new(intern(s))),
rhs: Box::new(r),
implicit_args: Vec::new(),
})
}

#[test]
fn take_implicits_into_account_on_infix_type() {
let mut expr = binop(int(1), "+", int(2));
if let Expr::Infix { ref mut op, .. } = expr.value {
op.value.typ = Type::function_implicit(
vec![Type::int()],
Type::function(vec![Type::int(), Type::int()], Type::int()),
);
}

assert_eq!(expr.env_type_of(&MockEnv), Type::int());
}
2 changes: 1 addition & 1 deletion repl/tests/rexpect.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ fn expression_types() {
let mut repl = REPL::new();

repl.test(":t 5", Some("Int"));
// repl.test(":t 5 + 5", Some("Int -> Int"));
repl.test(":t 5 + 5", Some("Int"));
repl.test(":t \"gluon\"", Some("String"));
}

Expand Down

0 comments on commit 8651be3

Please sign in to comment.