Skip to content

Commit

Permalink
feat: Implement unification of row polymorphic records
Browse files Browse the repository at this point in the history
  • Loading branch information
Marwes committed Sep 6, 2016
1 parent aeeb58b commit df007c6
Show file tree
Hide file tree
Showing 12 changed files with 269 additions and 125 deletions.
51 changes: 34 additions & 17 deletions base/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -449,21 +449,28 @@ impl<Id, T> Type<Id, T>
}

pub fn record(types: Vec<Field<Id, Alias<Id, T>>>, fields: Vec<Field<Id, T>>) -> T {
Type::poly_record(types, fields, Type::empty_row())
}

pub fn poly_record(types: Vec<Field<Id, Alias<Id, T>>>,
fields: Vec<Field<Id, T>>,
rest: T)
-> T {
T::from(Type::Record {
types: types,
row: if fields.is_empty() {
Type::empty_row()
} else {
Type::extend_row(fields, Type::empty_row())
},
row: Type::extend_row(fields, rest),
})
}

pub fn extend_row(fields: Vec<Field<Id, T>>, rest: T) -> T {
T::from(Type::ExtendRow {
fields: fields,
rest: rest,
})
if fields.is_empty() {
rest
} else {
T::from(Type::ExtendRow {
fields: fields,
rest: rest,
})
}
}

pub fn empty_row() -> T {
Expand Down Expand Up @@ -557,15 +564,19 @@ impl<Id, T> Type<Id, T>
_ => None,
}
}
}

pub fn field_iter(&self) -> FieldIterator<Id, T> {
pub trait TypeRef: Sized {
fn field_iter(&self) -> FieldIterator<Self> {
FieldIterator {
typ: self,
current: 0,
}
}
}

impl<Id, T> TypeRef for T where T: Deref<Target = Type<Id, T>> {}

impl<T> Type<Symbol, T>
where T: Deref<Target = Type<Symbol, T>>,
{
Expand All @@ -589,29 +600,35 @@ impl<T> Type<Symbol, T>
}
}

pub struct FieldIterator<'a, Id: 'a, T: 'a> {
typ: &'a Type<Id, T>,
pub struct FieldIterator<'a, T: 'a> {
typ: &'a T,
current: usize,
}

impl<'a, Id, T> Iterator for FieldIterator<'a, Id, T>
impl<'a, T> FieldIterator<'a, T> {
pub fn current_type(&self) -> &'a T {
self.typ
}
}

impl<'a, Id: 'a, T> Iterator for FieldIterator<'a, T>
where T: Deref<Target = Type<Id, T>>
{
type Item = &'a Field<Id, T>;

fn next(&mut self) -> Option<&'a Field<Id, T>> {
match *self.typ {
match **self.typ {
Type::Record { ref row, .. } => {
self.typ = &**row;
self.typ = row;
self.next()
},
}
Type::ExtendRow { ref fields, ref rest } => {
let current = self.current;
self.current += 1;
fields.get(current)
.or_else(|| {
self.current = 0;
self.typ = &**rest;
self.typ = rest;
self.next()
})
}
Expand Down
7 changes: 5 additions & 2 deletions check/src/rename.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use base::error::Errors;
use base::fnv::FnvMap;
use base::scoped_map::ScopedMap;
use base::symbol::{Symbol, SymbolRef, SymbolModule};
use base::types::{self, Alias, ArcType, Type, RcKind, KindEnv, TypeEnv};
use base::types::{self, Alias, ArcType, Type, TypeRef, RcKind, KindEnv, TypeEnv};
use unify_type::{TypeError, State};
use unify::{Error as UnifyError, Unifier, Unifiable, UnifierState};

Expand Down Expand Up @@ -319,8 +319,11 @@ pub fn rename(symbols: &mut SymbolModule,
}

pub fn equivalent(env: &TypeEnv, actual: &ArcType, inferred: &ArcType) -> bool {
use substitution::Substitution;
// FIXME Inneficient and possible wrong
let subs = Substitution::new();
let mut unifier = UnifierState {
state: State::new(env),
state: State::new(env, &subs),
unifier: Equivalent {
map: FnvMap::default(),
equiv: true,
Expand Down
3 changes: 2 additions & 1 deletion check/src/substitution.rs
Original file line number Diff line number Diff line change
Expand Up @@ -290,7 +290,8 @@ impl<T: Substitutable + PartialEq + Clone> Substitution<T> {
pub fn union(&self, id: &T::Variable, typ: &T) -> Result<(), ()>
where T::Variable: Clone,
{
// Nothing needs to be done if both are the same variable already (also prevents the occurs check from failing)
// Nothing needs to be done if both are the same variable already (also prevents the occurs
// check from failing)
if typ.get_var().map_or(false, |other| other.get_id() == id.get_id()) {
return Ok(());
}
Expand Down
71 changes: 52 additions & 19 deletions check/src/typecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use base::error::Errors;
use base::instantiate::{self, Instantiator};
use base::pos::{BytePos, Span, Spanned};
use base::symbol::{Symbol, SymbolRef, SymbolModule, Symbols};
use base::types::{self, ArcType, RcKind, Type, Generic, Kind, merge};
use base::types::{self, ArcType, Field, RcKind, Type, TypeRef, Generic, Kind, merge};
use base::types::{KindEnv, TypeEnv, PrimitiveEnv, Alias, AliasData, TypeVariable};
use kindcheck::{self, KindCheck};
use substitution::Substitution;
Expand Down Expand Up @@ -576,31 +576,39 @@ impl<'a> Typecheck<'a> {
try!(self.typecheck_bindings(bindings));
Ok(TailCall::TailCall)
}
Expr::Projection(ref mut expr, ref field_id, ref mut field_typ) => {
Expr::Projection(ref mut expr, ref field_id, ref mut ast_field_typ) => {
let mut expr_typ = self.typecheck(&mut **expr);
debug!("Projection {} . {:?}",
types::display_type(&self.symbols, &expr_typ),
self.symbols.string(field_id));
self.subs.make_real(&mut expr_typ);
if let Type::Variable(_) = *expr_typ {
// Attempt to find a record with `field_access` since inferring to a record
// with only `field_access` as the field is probably useless
let (record_type, _) = try!(self.find_record(&[field_id.clone()])
.map(|t| (t.0.clone(), t.1.clone())));
let record_type = self.instantiate(&record_type);
expr_typ = try!(self.unify(&record_type, expr_typ));
}

let record = self.remove_aliases(expr_typ.clone());
match *record {
Type::Variable(..) |
Type::Record { .. } => {
let field_type = record.field_iter()
.find(|field| field.name.name_eq(field_id))
.map(|field| field.typ.clone());
*field_typ = match field_type {
*ast_field_typ = match field_type {
Some(typ) => self.instantiate(&typ),
None => return Err(UndefinedField(expr_typ.clone(), field_id.clone())),
None => {
// FIXME As the polymorphic `record_type` do not have the type
// fields which `typ` this unification is only done after we
// checked if the field exists which lets field accesses on
// types with type fields still work
let field_var = self.subs.new_var();
let field = Field {
name: field_id.clone(),
typ: field_var.clone(),
};
let record_type =
Type::poly_record(vec![], vec![field], self.subs.new_var());
try!(self.unify(&record_type, record));
field_var
}
};
Ok(TailCall::Type(field_typ.clone()))
Ok(TailCall::Type(ast_field_typ.clone()))
}
_ => Err(InvalidProjection(record)),
}
Expand Down Expand Up @@ -1037,7 +1045,7 @@ impl<'a> Typecheck<'a> {
debug!("Intersect\n{} <> {}",
types::display_type(&self.symbols, existing_type),
types::display_type(&self.symbols, symbol_type));
let state = unify_type::State::new(&self.environment);
let state = unify_type::State::new(&self.environment, &self.subs);
let result = unify::intersection(&self.subs, state, existing_type, symbol_type);
debug!("Intersect result {}", result);
result
Expand Down Expand Up @@ -1224,7 +1232,7 @@ impl<'a> Typecheck<'a> {
expected: &ArcType,
mut actual: ArcType)
-> ArcType {
let state = unify_type::State::new(&self.environment);
let state = unify_type::State::new(&self.environment, &self.subs);
match unify_type::merge_signature(&self.subs,
&mut self.type_variables,
level,
Expand Down Expand Up @@ -1264,7 +1272,7 @@ impl<'a> Typecheck<'a> {
debug!("Unify {} <=> {}",
types::display_type(&self.symbols, expected),
types::display_type(&self.symbols, &actual));
let state = unify_type::State::new(&self.environment);
let state = unify_type::State::new(&self.environment, &self.subs);
match unify::unify(&self.subs, state, expected, &actual) {
Ok(typ) => Ok(self.subs.set_type(typ)),
Err(errors) => {
Expand Down Expand Up @@ -1312,8 +1320,8 @@ fn with_pattern_types<F>(fields: &[(Symbol, Option<Symbol>)], typ: &ArcType, mut
if let Type::Record { ref row, .. } = **typ {
if let Type::ExtendRow { fields: ref field_types, .. } = **row {
for field in fields {
// If the field in the pattern does not exist (undefined field error) then skip it as
// the error itself will already have been reported
// If the field in the pattern does not exist (undefined field error) then skip it
// as the error itself will already have been reported
if let Some(associated_type) = field_types.iter()
.find(|type_field| type_field.name.name_eq(&field.0)) {
f(&field.0, &field.1, &associated_type.typ);
Expand Down Expand Up @@ -1455,7 +1463,7 @@ pub fn unroll_app(typ: &Type<Symbol>) -> Option<ArcType> {
args.extend(rest.iter().rev().cloned());
l
}
_ => return None,
_ => return unroll_record(typ),
};
while let Type::App(ref l, ref rest) = **current {
args.extend(rest.iter().rev().cloned());
Expand All @@ -1468,3 +1476,28 @@ pub fn unroll_app(typ: &Type<Symbol>) -> Option<ArcType> {
Some(Type::app(current.clone(), args))
}
}

pub fn unroll_record(typ: &Type<Symbol>) -> Option<ArcType> {
let mut args = Vec::new();
let mut current = match *typ {
Type::ExtendRow { ref fields, ref rest } => {
match **rest {
Type::ExtendRow { .. } => {
args.extend_from_slice(fields);
rest
}
_ => return None,
}
}
_ => return None,
};
while let Type::ExtendRow { ref fields, ref rest } = **current {
args.extend_from_slice(fields);
current = rest;
}
if args.is_empty() {
None
} else {
Some(Type::extend_row(args, current.clone()))
}
}
Loading

0 comments on commit df007c6

Please sign in to comment.