Skip to content

Commit

Permalink
fix: Do not traverse the replacement type in walk_move_type
Browse files Browse the repository at this point in the history
When using walk_move_type to instantiate a type such as `State s (a -> b)` where `type State s a = s -> { state: s, value: a }` the default behaviour of walking the replacement type would cause a stack overflow due to:

Instantiate: a ==> (a -> b)
Walk: (a -> b)
Instantiate: a ==> (a -> b)
Walk: (a -> b)
etc.

There are one case when walking the replacement is desired but that is easily fixed by recursively walking the replacement before returning it (which should be more efficient as well).
  • Loading branch information
Marwes committed Jul 22, 2016
1 parent 8e8d476 commit 1fbb4af
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 48 deletions.
93 changes: 48 additions & 45 deletions base/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -910,7 +910,7 @@ pub fn walk_move_type<F, I, T>(typ: T, f: &mut F) -> T
T: Deref<Target = Type<I, T>> + From<Type<I, T>> + Clone,
I: Clone
{
walk_move_type2(&typ, f).unwrap_or(typ)
walk_move_type_opt(&typ, f).unwrap_or(typ)
}

/// Merges two values using `f` if either or both them is `Some(..)`.
Expand All @@ -933,57 +933,60 @@ pub fn merge<F, A, B, R>(a_original: &A,
}
}

fn walk_move_type2<F, I, T>(typ: &Type<I, T>, f: &mut F) -> Option<T>
pub fn walk_move_type_opt<F, I, T>(typ: &Type<I, T>, f: &mut F) -> Option<T>
where F: FnMut(&Type<I, T>) -> Option<T>,
T: Deref<Target = Type<I, T>> + From<Type<I, T>> + Clone,
I: Clone
{
let new = f(typ);
let result = {
let typ = new.as_ref().map_or(typ, |t| &**t);
match *typ {
Type::App(ref id, ref args) => {
let new_args = walk_move_types(args.iter(), |t| walk_move_type2(t, f));
merge(id, walk_move_type2(id, f), args, new_args, Type::app)
}
Type::Array(ref inner) => walk_move_type2(&**inner, f).map(Type::array),
Type::Function(ref args, ref ret) => {
let new_args = walk_move_types(args.iter(), |t| walk_move_type2(t, f));
merge(args, new_args, ret, walk_move_type2(ret, f), Type::Function).map(From::from)
}
Type::Record { ref types, ref fields } => {
let new_types = None;
let new_fields = walk_move_types(fields.iter(), |field| {
walk_move_type2(&field.typ, f).map(|typ| {
Field {
name: field.name.clone(),
typ: typ,
}
})
});
merge(types, new_types, fields, new_fields, |types, fields| {
Type::Record {
types: types,
fields: fields,
}
})
.map(From::from)
}
Type::Variants(ref variants) => {
walk_move_types(variants.iter(),
|v| walk_move_type2(&v.1, f).map(|t| (v.0.clone(), t)))
.map(Type::Variants)
.map(From::from)
}
Type::Builtin(_) |
Type::Variable(_) |
Type::Generic(_) |
Type::Id(_) |
Type::Alias(_) => None,
let new_type = match *typ {
Type::App(ref id, ref args) => {
let new_args = walk_move_types(args.iter(), |t| walk_move_type_opt(t, f));
merge(id, walk_move_type_opt(id, f), args, new_args, Type::app)
}
Type::Array(ref inner) => walk_move_type_opt(&**inner, f).map(Type::array),
Type::Function(ref args, ref ret) => {
let new_args = walk_move_types(args.iter(), |t| walk_move_type_opt(t, f));
merge(args,
new_args,
ret,
walk_move_type_opt(ret, f),
Type::Function)
.map(From::from)
}
Type::Record { ref types, ref fields } => {
let new_types = None;
let new_fields = walk_move_types(fields.iter(), |field| {
walk_move_type_opt(&field.typ, f).map(|typ| {
Field {
name: field.name.clone(),
typ: typ,
}
})
});
merge(types, new_types, fields, new_fields, |types, fields| {
Type::Record {
types: types,
fields: fields,
}
})
.map(From::from)
}
Type::Variants(ref variants) => {
walk_move_types(variants.iter(),
|v| walk_move_type_opt(&v.1, f).map(|t| (v.0.clone(), t)))
.map(Type::Variants)
.map(From::from)
}
Type::Builtin(_) |
Type::Variable(_) |
Type::Generic(_) |
Type::Id(_) |
Type::Alias(_) => None,
};
result.or(new)
let new_type2 = f(new_type.as_ref().map_or(typ, |t| t));
new_type2.or(new_type)
}

fn walk_move_types<'a, I, F, T>(types: I, mut f: F) -> Option<Vec<T>>
where I: Iterator<Item = &'a T>,
F: FnMut(&'a T) -> Option<T>,
Expand Down
12 changes: 9 additions & 3 deletions check/src/typecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,9 +1063,15 @@ impl<'a> Typecheck<'a> {
String::from(max_var)
};
let mut i = 0;
self.finish_type_(level, &generic, &mut i, typ)
}

fn finish_type_(&mut self, level: u32, generic: &str, i: &mut i32, typ: TcType) -> TcType {
types::walk_move_type(typ,
&mut |typ| {
let replacement = self.subs.replace_variable(typ);
&mut |typ| {
let replacement = self.subs
.replace_variable(typ)
.map(|t| self.finish_type_(level, generic, i, t));
let mut typ = typ;
if let Some(ref t) = replacement {
debug!("{} ==> {}",
Expand All @@ -1076,7 +1082,7 @@ impl<'a> Typecheck<'a> {
match *typ {
Type::Variable(ref var) if self.subs.get_level(var.id) > level => {
let generic = format!("{}{}", generic, i);
i += 1;
*i += 1;
let id = self.symbols.symbol(generic);
let gen: TcType = Type::generic(Generic {
kind: var.kind.clone(),
Expand Down

0 comments on commit 1fbb4af

Please sign in to comment.