Skip to content

Commit

Permalink
perf(check): Only add implicit fields if the binding is implicit
Browse files Browse the repository at this point in the history
When encountering a binding we no longer recurse through the entire
record but instead only do it as long as we are in a implicit binding
already. Thus adding `Applicative` also adds its `Functor` field since
`Applicative` is implicit. However, for a normal record such as from
`let int = import! std.int` we no longer recurse and add all the
implicit instances in `std.int`, instead a `let int @ { ? }` is
required.

BREAKING CHANGE
  • Loading branch information
Marwes committed Aug 31, 2019
1 parent 06ef80d commit da861eb
Show file tree
Hide file tree
Showing 3 changed files with 66 additions and 51 deletions.
35 changes: 26 additions & 9 deletions base/src/resolve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::borrow::Cow;
use crate::{
fnv::FnvMap,
symbol::Symbol,
types::{AliasRef, Type, TypeContext, TypeEnv, TypeExt},
types::{AliasData, AliasRef, Type, TypeContext, TypeEnv, TypeExt},
};

quick_error! {
Expand Down Expand Up @@ -106,7 +106,7 @@ impl<T> AliasRemover<T> {
T: TypeExt<Id = Symbol> + ::std::fmt::Display,
{
loop {
typ = match self.remove_alias_to_concrete(env, interner, &typ)? {
typ = match self.remove_alias_to_concrete(env, interner, &typ, |_| true)? {
Some((typ, args)) => match *typ {
Type::Builtin(..)
| Type::Function(..)
Expand Down Expand Up @@ -134,16 +134,29 @@ impl<T> AliasRemover<T> {
}

pub fn remove_aliases(
&mut self,
env: &dyn TypeEnv<Type = T>,
interner: &mut impl TypeContext<Symbol, T>,
typ: T,
) -> Result<T, Error>
where
T: TypeExt<Id = Symbol> + ::std::fmt::Display,
{
self.remove_aliases_predicate(env, interner, typ, |_| true)
}

pub fn remove_aliases_predicate(
&mut self,
env: &dyn TypeEnv<Type = T>,
interner: &mut impl TypeContext<Symbol, T>,
mut typ: T,
mut predicate: impl FnMut(&AliasData<Symbol, T>) -> bool,
) -> Result<T, Error>
where
T: TypeExt<Id = Symbol> + ::std::fmt::Display,
{
loop {
typ = match self.remove_alias(env, interner, &typ)? {
typ = match self.remove_alias(env, interner, &typ, &mut predicate)? {
Some(typ) => typ,
None => return Ok(typ),
};
Expand All @@ -155,33 +168,37 @@ impl<T> AliasRemover<T> {
env: &dyn TypeEnv<Type = T>,
interner: &mut impl TypeContext<Symbol, T>,
typ: &T,
predicate: impl FnOnce(&AliasData<Symbol, T>) -> bool,
) -> Result<Option<T>, Error>
where
T: TypeExt<Id = Symbol> + ::std::fmt::Display,
{
Ok(self.remove_alias_to_concrete(env, interner, typ)?.map(
|(non_replaced_type, unapplied_args)| {
Ok(self
.remove_alias_to_concrete(env, interner, typ, predicate)?
.map(|(non_replaced_type, unapplied_args)| {
let non_replaced_type = non_replaced_type
.replace_generics(interner, &mut self.named_variables)
.unwrap_or_else(|| non_replaced_type.clone());

interner.app(non_replaced_type, unapplied_args.iter().cloned().collect())
},
))
}))
}

pub fn remove_alias_to_concrete<'a>(
&mut self,
env: &'a dyn TypeEnv<Type = T>,
interner: &mut impl TypeContext<Symbol, T>,
typ: &'a T,
predicate: impl FnOnce(&AliasData<Symbol, T>) -> bool,
) -> Result<Option<(T, Cow<'a, [T]>)>, Error>
where
T: TypeExt<Id = Symbol> + ::std::fmt::Display,
{
match peek_alias(env, &typ)? {
Some(alias) => self.remove_alias_to_concrete_inner(interner, typ, alias),
None => Ok(None),
Some(alias) if predicate(alias) => {
self.remove_alias_to_concrete_inner(interner, typ, alias)
}
_ => Ok(None),
}
}

Expand Down
58 changes: 40 additions & 18 deletions check/src/implicits.rs
Original file line number Diff line number Diff line change
Expand Up @@ -776,7 +776,17 @@ impl<'a> ImplicitResolver<'a> {
}

pub fn on_stack_var(&mut self, subs: &Substitution<RcType>, id: &Symbol, typ: &RcType) {
self.add_implicits_of_record(subs, id, typ);
self.alias_resolver.clear();

self.path.clear();
self.path.push(TypedIdent {
name: id.clone(),
typ: typ.clone(),
});

let meta = self.metadata.get(id).cloned();

self.add_implicits_of_ident(subs, typ, meta.as_ref().map(|m| &**m), &mut Vec::new());
}

pub fn add_implicits_of_record(
Expand All @@ -797,7 +807,7 @@ impl<'a> ImplicitResolver<'a> {
self.add_implicits_of_record_rec(subs, typ, meta.as_ref().map(|m| &**m), &mut Vec::new());
}

fn add_implicits_of_record_rec(
fn add_implicits_of_ident(
&mut self,
mut subs: &Substitution<RcType>,
typ: &RcType,
Expand All @@ -821,30 +831,47 @@ impl<'a> ImplicitResolver<'a> {

let opt = self.try_create_implicit(metadata, typ);

let mut typ = typ.clone();
if let Some(definition) = opt {
let typ = subs.forall(forall_params.iter().cloned().collect(), typ.clone());

self.implicit_bindings
.insert(subs, definition, &self.path, &typ);

self.add_implicits_of_record_rec(subs, &typ, metadata, forall_params)
}
}

fn add_implicits_of_record_rec(
&mut self,
mut subs: &Substitution<RcType>,
typ: &RcType,
metadata: Option<&Metadata>,
forall_params: &mut Vec<Generic<Symbol>>,
) {
let forall_params_len_before = forall_params.len();

let mut typ = typ.clone();
while let Type::Forall(params, next) = &*typ {
forall_params.extend(params.iter().cloned());
typ = next.clone();
}

let raw_type =
match self
.alias_resolver
.remove_aliases(&self.environment, &mut subs, typ.clone())
{
Ok(t) => t,
// Don't recurse into self recursive aliases
Err(_) => return,
};
let t = self.alias_resolver.remove_aliases_predicate(
&self.environment,
&mut subs,
typ.clone(),
|alias| {
alias
.unresolved_type()
.flags()
.contains(Flags::HAS_IMPLICIT)
},
);
let raw_type = match t {
Ok(t) => t,
// Don't recurse into self recursive aliases
Err(_) => return,
};
match *raw_type {
Type::Record(_) => {
for field in raw_type.row_iter() {
Expand All @@ -861,12 +888,7 @@ impl<'a> ImplicitResolver<'a> {
typ: field.typ.clone(),
});

self.add_implicits_of_record_rec(
subs,
&field.typ,
field_metadata,
forall_params,
);
self.add_implicits_of_ident(subs, &field.typ, field_metadata, forall_params);

self.path.pop();
}
Expand Down
24 changes: 0 additions & 24 deletions tests/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -409,30 +409,6 @@ f 0 (\r -> { x = r #Int+ 1 })
1i32
}

#[test]
fn overloaded_bindings() {
let _ = ::env_logger::try_init();
let text = r#"
#[implicit]
let add_int x y = x #Int+ y
#[implicit]
let add_float x y = x #Float+ y
let add ?f: [a -> a -> a] -> a -> a -> a = f
{ x = add 1 2, y = add 1.0 2.0 }
"#;
let vm = make_vm();
let result = run_expr::<OpaqueValue<&Thread, Hole>>(&vm, text);
match result.get_ref() {
ValueRef::Data(data) => {
assert_eq!(data.get(0).unwrap(), ValueRef::Int(3));
assert_eq!(data.get(1).unwrap(), ValueRef::Float(3.0));
}
_ => panic!(),
}
}

test_expr! { record_base_duplicate_fields,
r#"
{ x = "" .. { x = 1 } }.x
Expand Down

0 comments on commit da861eb

Please sign in to comment.