Skip to content

Commit

Permalink
fix(check): Handle unification with Type::Hole
Browse files Browse the repository at this point in the history
`Type::Hole` needs to be eagerly instantiated with a type variable to ensure that it is only ever instantiated with a single variable and to ensure that the hole does not leak into the normal unification functions.
  • Loading branch information
Marwes authored and brendanzab committed Sep 4, 2016
1 parent b1fcd84 commit 2912727
Show file tree
Hide file tree
Showing 5 changed files with 17 additions and 10 deletions.
2 changes: 1 addition & 1 deletion check/src/kindcheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ impl<'a> KindCheck<'a> {
gen.kind = try!(self.find(&gen.id));
Ok((gen.kind.clone(), Type::generic(gen)))
}
Type::Variable(_) => panic!("kindcheck called on variable"),
Type::Variable(_) => Ok((self.subs.new_var(), typ.clone())),
Type::Builtin(builtin_typ) => Ok((self.builtin_kind(builtin_typ), typ.clone())),
Type::App(ref ctor, ref args) => {
let (mut kind, ctor) = try!(self.kindcheck(ctor));
Expand Down
18 changes: 13 additions & 5 deletions check/src/typecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -636,7 +636,7 @@ impl<'a> Typecheck<'a> {
let types = try!(types.iter_mut()
.map(|&mut (ref mut symbol, ref mut typ)| {
if let Some(ref mut typ) = *typ {
*typ = self.refresh_symbols_in_type(typ.clone());
*typ = self.create_unifiable_signature(typ.clone());
}
let alias = try!(self.find_type_info(symbol));

Expand Down Expand Up @@ -856,7 +856,7 @@ impl<'a> Typecheck<'a> {
if is_recursive {
for bind in bindings.iter_mut() {
let typ = {
bind.typ = self.refresh_symbols_in_type(bind.typ.clone());
bind.typ = self.create_unifiable_signature(bind.typ.clone());
try!(self.kindcheck(&mut bind.typ));
self.instantiate_signature(&bind.typ)
};
Expand All @@ -876,7 +876,7 @@ impl<'a> Typecheck<'a> {
// recursive
let mut typ = if bind.arguments.is_empty() {
self.instantiate_signature(&bind.typ);
bind.typ = self.refresh_symbols_in_type(bind.typ.clone());
bind.typ = self.create_unifiable_signature(bind.typ.clone());
try!(self.kindcheck(&mut bind.typ));
self.typecheck(&mut bind.expression)
} else {
Expand Down Expand Up @@ -912,6 +912,9 @@ impl<'a> Typecheck<'a> {
debug!("Generalize {}", level);
for bind in bindings {
self.generalize_variables(level, &mut bind.expression);
if let Some(typ) = self.finish_type(level, &bind.typ) {
bind.typ = typ;
}
self.finish_binding(level, bind);
}
debug!("Typecheck `in`");
Expand All @@ -938,7 +941,7 @@ impl<'a> Typecheck<'a> {
.typ
.as_mut()
.expect("Expected binding to have an aliased type");
*typ = self.refresh_symbols_in_type(typ.clone());
*typ = self.create_unifiable_signature(typ.clone());
}
{
let subs = Substitution::new();
Expand Down Expand Up @@ -1165,7 +1168,11 @@ impl<'a> Typecheck<'a> {
typ
}

fn refresh_symbols_in_type(&mut self, typ: ArcType) -> ArcType {
// Replaces `Type::Id` types with the actual `Type::Alias` type it refers to
// Replaces variant names with the actual symbol they should refer to
// Instantiates Type::Hole with a fresh type variable to ensure the hole only ever refers to a
// single type variable
fn create_unifiable_signature(&mut self, typ: ArcType) -> ArcType {
let mut f = |typ: &Type<Symbol, ArcType>| {
match *typ {
Type::Alias(ref alias) => {
Expand Down Expand Up @@ -1218,6 +1225,7 @@ impl<'a> Typecheck<'a> {
None
}
}
Type::Hole => Some(self.subs.new_var()),
_ => None,
}
};
Expand Down
3 changes: 0 additions & 3 deletions check/src/unify_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -425,9 +425,6 @@ impl<'a, 'e> Unifier<State<'a>, ArcType> for Merge<'e> {
// `l` and `r` must have the same type, if one is a variable that variable is
// unified with whatever the other type is
let result = match (&**l, &**r) {
(&Type::Hole, &Type::Hole) => Ok(Some(subs.new_var())),
(_, &Type::Hole) => Ok(Some(l.clone())),
(&Type::Hole, _) => Ok(Some(r.clone())),
(&Type::Variable(ref l), &Type::Variable(ref r)) if l.id == r.id => Ok(None),
(&Type::Generic(ref l_gen), &Type::Variable(ref r_var)) => {
let left = match unifier.unifier.variables.get(&l_gen.id) {
Expand Down
2 changes: 2 additions & 0 deletions check/tests/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ let f x = f x

#[test]
fn binop() {
let _ = env_logger::init();

let env = MockEnv::new();

let (mut expr, result) = support::typecheck_expr(r#"
Expand Down
2 changes: 1 addition & 1 deletion check/tests/pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ macro_rules! assert_match {
($i: expr, $p: pat => $e: expr) => {
match $i {
$p => $e,
ref x => assert!(false, "Unexpected {}, found {:?}", stringify!($p), x)
ref x => assert!(false, "Expected {}, found {:?}", stringify!($p), x)
}
};
}
Expand Down

0 comments on commit 2912727

Please sign in to comment.