mirror of
https://github.com/roc-lang/roc.git
synced 2025-10-02 08:11:12 +00:00
commit
b47522489c
8 changed files with 529 additions and 77 deletions
|
@ -170,6 +170,10 @@ impl<K: UnifyKey> VarValue<K> {
|
||||||
self.if_not_self(self.parent, self_key)
|
self.if_not_self(self.parent, self_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn raw_parent(&self) -> K {
|
||||||
|
self.parent
|
||||||
|
}
|
||||||
|
|
||||||
fn if_not_self(&self, key: K, self_key: K) -> Option<K> {
|
fn if_not_self(&self, key: K, self_key: K) -> Option<K> {
|
||||||
if key == self_key {
|
if key == self_key {
|
||||||
None
|
None
|
||||||
|
@ -267,20 +271,29 @@ impl<S: UnificationStore> UnificationTable<S> {
|
||||||
/// NB. This is a building-block operation and you would probably
|
/// NB. This is a building-block operation and you would probably
|
||||||
/// prefer to call `probe` below.
|
/// prefer to call `probe` below.
|
||||||
pub fn get_root_key(&mut self, vid: S::Key) -> S::Key {
|
pub fn get_root_key(&mut self, vid: S::Key) -> S::Key {
|
||||||
let redirect = {
|
match self.value(vid).parent(vid) {
|
||||||
match self.value(vid).parent(vid) {
|
None => vid,
|
||||||
None => return vid,
|
Some(redirect) => {
|
||||||
Some(redirect) => redirect,
|
let root_key: S::Key = self.get_root_key(redirect);
|
||||||
|
if root_key != redirect {
|
||||||
|
// Path compression
|
||||||
|
self.update_value(vid, |value| value.parent = root_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
root_key
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
let root_key: S::Key = self.get_root_key(redirect);
|
|
||||||
if root_key != redirect {
|
|
||||||
// Path compression
|
|
||||||
self.update_value(vid, |value| value.parent = root_key);
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
root_key
|
pub fn get_root_key_without_compacting(&self, vid: S::Key) -> S::Key {
|
||||||
|
match self.value(vid).parent(vid) {
|
||||||
|
None => vid,
|
||||||
|
Some(redirect) => self.get_root_key_without_compacting(redirect),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_redirect(&mut self, vid: S::Key) -> bool {
|
||||||
|
self.value(vid).raw_parent() != vid
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update_value<OP>(&mut self, key: S::Key, op: OP)
|
pub fn update_value<OP>(&mut self, key: S::Key, op: OP)
|
||||||
|
@ -400,4 +413,15 @@ where
|
||||||
let id = self.get_root_key(id);
|
let id = self.get_root_key(id);
|
||||||
self.value(id).value.clone()
|
self.value(id).value.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// This is for a debug_assert! in solve() only. Do not use it elsewhere!
|
||||||
|
pub fn probe_value_without_compacting<K1>(&self, id: K1) -> V
|
||||||
|
where
|
||||||
|
K1: Into<K>,
|
||||||
|
{
|
||||||
|
let id = id.into();
|
||||||
|
let id = self.get_root_key_without_compacting(id);
|
||||||
|
|
||||||
|
self.value(id).value.clone()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::collections::ImMap;
|
use crate::collections::ImMap;
|
||||||
use crate::solve::solve;
|
use crate::solve;
|
||||||
use crate::subs::{Content, Subs, Variable};
|
use crate::subs::{Content, Subs, Variable};
|
||||||
use crate::types::Constraint;
|
use crate::types::Constraint;
|
||||||
use crate::unify::Problems;
|
use crate::unify::Problems;
|
||||||
|
@ -10,7 +10,7 @@ pub fn infer_expr(
|
||||||
constraint: &Constraint,
|
constraint: &Constraint,
|
||||||
expr_var: Variable,
|
expr_var: Variable,
|
||||||
) -> Content {
|
) -> Content {
|
||||||
solve(&ImMap::default(), problems, subs, constraint);
|
solve::run(&ImMap::default(), problems, subs, constraint);
|
||||||
|
|
||||||
subs.get(expr_var).content
|
subs.get(expr_var).content
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ use crate::parse::ast::{self, Attempting, ExposesEntry, ImportsEntry};
|
||||||
use crate::parse::module::{self, module_defs};
|
use crate::parse::module::{self, module_defs};
|
||||||
use crate::parse::parser::{Fail, Parser, State};
|
use crate::parse::parser::{Fail, Parser, State};
|
||||||
use crate::region::{Located, Region};
|
use crate::region::{Located, Region};
|
||||||
use crate::solve::solve;
|
use crate::solve;
|
||||||
use crate::subs::VarStore;
|
use crate::subs::VarStore;
|
||||||
use crate::subs::{Subs, Variable};
|
use crate::subs::{Subs, Variable};
|
||||||
use crate::types::Constraint;
|
use crate::types::Constraint;
|
||||||
|
@ -414,8 +414,8 @@ pub fn solve_loaded(
|
||||||
}
|
}
|
||||||
|
|
||||||
for constraint in constraints {
|
for constraint in constraints {
|
||||||
solve(&vars_by_symbol, problems, subs, &constraint);
|
solve::run(&vars_by_symbol, problems, subs, &constraint);
|
||||||
}
|
}
|
||||||
|
|
||||||
solve(&vars_by_symbol, problems, subs, &module.constraint);
|
solve::run(&vars_by_symbol, problems, subs, &module.constraint);
|
||||||
}
|
}
|
||||||
|
|
381
src/solve.rs
381
src/solve.rs
|
@ -2,7 +2,7 @@ use crate::can::ident::Lowercase;
|
||||||
use crate::can::symbol::Symbol;
|
use crate::can::symbol::Symbol;
|
||||||
use crate::collections::ImMap;
|
use crate::collections::ImMap;
|
||||||
use crate::region::Located;
|
use crate::region::Located;
|
||||||
use crate::subs::{Content, Descriptor, FlatType, Subs, Variable};
|
use crate::subs::{Content, Descriptor, FlatType, Mark, Rank, Subs, Variable};
|
||||||
use crate::types::Constraint::{self, *};
|
use crate::types::Constraint::{self, *};
|
||||||
use crate::types::Problem;
|
use crate::types::Problem;
|
||||||
use crate::types::Type::{self, *};
|
use crate::types::Type::{self, *};
|
||||||
|
@ -10,20 +10,100 @@ use crate::unify::{unify, Problems};
|
||||||
|
|
||||||
type Env = ImMap<Symbol, Variable>;
|
type Env = ImMap<Symbol, Variable>;
|
||||||
|
|
||||||
pub fn solve(
|
const DEFAULT_POOLS: usize = 8;
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Pools(Vec<Vec<Variable>>);
|
||||||
|
|
||||||
|
impl Default for Pools {
|
||||||
|
fn default() -> Self {
|
||||||
|
Pools::new(DEFAULT_POOLS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Pools {
|
||||||
|
pub fn new(num_pools: usize) -> Self {
|
||||||
|
let mut pools = Vec::with_capacity(num_pools);
|
||||||
|
|
||||||
|
for _ in 0..num_pools {
|
||||||
|
pools.push(Vec::new());
|
||||||
|
}
|
||||||
|
|
||||||
|
Pools(pools)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_mut(&mut self, rank: Rank) -> &mut Vec<Variable> {
|
||||||
|
self.0
|
||||||
|
.get_mut(rank.into_usize())
|
||||||
|
.unwrap_or_else(|| panic!("Compiler bug: could not find pool at rank {}", rank))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get(&self, rank: Rank) -> &Vec<Variable> {
|
||||||
|
self.0
|
||||||
|
.get(rank.into_usize())
|
||||||
|
.unwrap_or_else(|| panic!("Compiler bug: could not find pool at rank {}", rank))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn iter<'a>(&'a self) -> std::slice::Iter<'a, Vec<Variable>> {
|
||||||
|
self.0.iter()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn split_last(&self) -> (&Vec<Variable>, &[Vec<Variable>]) {
|
||||||
|
self.0
|
||||||
|
.split_last()
|
||||||
|
.unwrap_or_else(|| panic!("Attempted to split_last() on non-empy Pools"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
vars_by_symbol: Env,
|
||||||
|
mark: Mark,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run(
|
||||||
vars_by_symbol: &Env,
|
vars_by_symbol: &Env,
|
||||||
problems: &mut Problems,
|
problems: &mut Problems,
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
constraint: &Constraint,
|
constraint: &Constraint,
|
||||||
) {
|
) {
|
||||||
|
let mut pools = Pools::default();
|
||||||
|
let state = State {
|
||||||
|
vars_by_symbol: vars_by_symbol.clone(),
|
||||||
|
mark: Mark::none().next(),
|
||||||
|
};
|
||||||
|
let rank = Rank::outermost();
|
||||||
|
|
||||||
|
solve(
|
||||||
|
vars_by_symbol,
|
||||||
|
state,
|
||||||
|
rank,
|
||||||
|
&mut pools,
|
||||||
|
problems,
|
||||||
|
subs,
|
||||||
|
constraint,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn solve(
|
||||||
|
vars_by_symbol: &Env,
|
||||||
|
state: State,
|
||||||
|
rank: Rank,
|
||||||
|
pools: &mut Pools,
|
||||||
|
problems: &mut Problems,
|
||||||
|
subs: &mut Subs,
|
||||||
|
constraint: &Constraint,
|
||||||
|
) -> State {
|
||||||
match constraint {
|
match constraint {
|
||||||
True => (),
|
True => state,
|
||||||
Eq(typ, expected_type, _region) => {
|
Eq(typ, expected_type, _region) => {
|
||||||
// TODO use region?
|
// TODO use region?
|
||||||
let actual = type_to_var(subs, typ.clone());
|
let actual = type_to_var(subs, typ.clone());
|
||||||
let expected = type_to_var(subs, expected_type.clone().get_type());
|
let expected = type_to_var(subs, expected_type.clone().get_type());
|
||||||
|
let vars = unify(subs, problems, actual, expected);
|
||||||
|
|
||||||
unify(subs, problems, actual, expected);
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
|
state
|
||||||
}
|
}
|
||||||
Lookup(symbol, expected_type, _region) => {
|
Lookup(symbol, expected_type, _region) => {
|
||||||
// TODO use region?
|
// TODO use region?
|
||||||
|
@ -36,29 +116,80 @@ pub fn solve(
|
||||||
)
|
)
|
||||||
}));
|
}));
|
||||||
let expected = type_to_var(subs, expected_type.clone().get_type());
|
let expected = type_to_var(subs, expected_type.clone().get_type());
|
||||||
|
let vars = unify(subs, problems, actual, expected);
|
||||||
|
|
||||||
unify(subs, problems, actual, expected);
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
|
state
|
||||||
}
|
}
|
||||||
And(sub_constraints) => {
|
And(sub_constraints) => {
|
||||||
|
let mut state = state;
|
||||||
|
|
||||||
for sub_constraint in sub_constraints.iter() {
|
for sub_constraint in sub_constraints.iter() {
|
||||||
solve(vars_by_symbol, problems, subs, sub_constraint);
|
state = solve(
|
||||||
|
vars_by_symbol,
|
||||||
|
state,
|
||||||
|
rank,
|
||||||
|
pools,
|
||||||
|
problems,
|
||||||
|
subs,
|
||||||
|
sub_constraint,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
state
|
||||||
}
|
}
|
||||||
Pattern(_region, _category, typ, expected) => {
|
Pattern(_region, _category, typ, expected) => {
|
||||||
// TODO use region?
|
// TODO use region?
|
||||||
let actual = type_to_var(subs, typ.clone());
|
let actual = type_to_var(subs, typ.clone());
|
||||||
let expected = type_to_var(subs, expected.clone().get_type());
|
let expected = type_to_var(subs, expected.clone().get_type());
|
||||||
|
let vars = unify(subs, problems, actual, expected);
|
||||||
|
|
||||||
unify(subs, problems, actual, expected);
|
introduce(subs, rank, pools, &vars);
|
||||||
|
|
||||||
|
state
|
||||||
}
|
}
|
||||||
Let(let_con) => {
|
Let(let_con) => {
|
||||||
match &let_con.ret_constraint {
|
match &let_con.ret_constraint {
|
||||||
True => {
|
True => {
|
||||||
|
introduce(subs, rank, pools, &let_con.flex_vars);
|
||||||
|
|
||||||
// If the return expression is guaranteed to solve,
|
// If the return expression is guaranteed to solve,
|
||||||
// solve the assignments themselves and move on.
|
// solve the assignments themselves and move on.
|
||||||
solve(vars_by_symbol, problems, subs, &let_con.defs_constraint)
|
solve(
|
||||||
|
vars_by_symbol,
|
||||||
|
state,
|
||||||
|
rank,
|
||||||
|
pools,
|
||||||
|
problems,
|
||||||
|
subs,
|
||||||
|
&let_con.defs_constraint,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
ret_con => {
|
ret_con => {
|
||||||
|
let rigid_vars = &let_con.rigid_vars;
|
||||||
|
let flex_vars = &let_con.flex_vars;
|
||||||
|
|
||||||
|
// work in the next pool to localize header
|
||||||
|
let next_rank = rank.next();
|
||||||
|
|
||||||
|
let mut next_pools = pools.clone();
|
||||||
|
|
||||||
|
// introduce variables
|
||||||
|
for &var in rigid_vars.iter() {
|
||||||
|
subs.set_rank(var, next_rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
for &var in flex_vars.iter() {
|
||||||
|
subs.set_rank(var, next_rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
let pool: &mut Vec<Variable> = next_pools.get_mut(next_rank);
|
||||||
|
|
||||||
|
pool.reserve(rigid_vars.len() + flex_vars.len());
|
||||||
|
pool.extend(rigid_vars.iter());
|
||||||
|
pool.extend(flex_vars.iter());
|
||||||
|
|
||||||
// Add a variable for each assignment to the vars_by_symbol.
|
// Add a variable for each assignment to the vars_by_symbol.
|
||||||
let mut locals = ImMap::default();
|
let mut locals = ImMap::default();
|
||||||
|
|
||||||
|
@ -74,8 +205,31 @@ pub fn solve(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// run solver in next pool
|
||||||
|
|
||||||
// Solve the assignments' constraints first.
|
// Solve the assignments' constraints first.
|
||||||
solve(vars_by_symbol, problems, subs, &let_con.defs_constraint);
|
let new_state = solve(
|
||||||
|
vars_by_symbol,
|
||||||
|
state,
|
||||||
|
next_rank,
|
||||||
|
&mut next_pools,
|
||||||
|
problems,
|
||||||
|
subs,
|
||||||
|
&let_con.defs_constraint,
|
||||||
|
);
|
||||||
|
let young_mark = new_state.mark;
|
||||||
|
let visit_mark = young_mark.next();
|
||||||
|
let final_mark = visit_mark.next();
|
||||||
|
|
||||||
|
// pop pool
|
||||||
|
generalize(subs, young_mark, visit_mark, next_rank, &mut next_pools);
|
||||||
|
|
||||||
|
next_pools.get_mut(next_rank).clear();
|
||||||
|
|
||||||
|
// check that things went well
|
||||||
|
debug_assert!(rigid_vars
|
||||||
|
.iter()
|
||||||
|
.all(|&var| subs.get_without_compacting(var).rank == Rank::none()));
|
||||||
|
|
||||||
let mut new_vars_by_symbol = vars_by_symbol.clone();
|
let mut new_vars_by_symbol = vars_by_symbol.clone();
|
||||||
|
|
||||||
|
@ -83,13 +237,30 @@ pub fn solve(
|
||||||
new_vars_by_symbol.insert(symbol.clone(), loc_var.value);
|
new_vars_by_symbol.insert(symbol.clone(), loc_var.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Note that this vars_by_symbol is the one returned by the
|
||||||
|
// previous call to solve()
|
||||||
|
let temp_state = State {
|
||||||
|
vars_by_symbol: new_state.vars_by_symbol,
|
||||||
|
mark: final_mark,
|
||||||
|
};
|
||||||
|
|
||||||
// Now solve the body, using the new vars_by_symbol which includes
|
// Now solve the body, using the new vars_by_symbol which includes
|
||||||
// the assignments' name-to-variable mappings.
|
// the assignments' name-to-variable mappings.
|
||||||
solve(&new_vars_by_symbol, problems, subs, &ret_con);
|
let new_state = solve(
|
||||||
|
&new_vars_by_symbol,
|
||||||
|
temp_state,
|
||||||
|
rank,
|
||||||
|
&mut next_pools,
|
||||||
|
problems,
|
||||||
|
subs,
|
||||||
|
&ret_con,
|
||||||
|
);
|
||||||
|
|
||||||
for (symbol, loc_var) in locals {
|
for (symbol, loc_var) in locals {
|
||||||
check_for_infinite_type(subs, problems, symbol, loc_var);
|
check_for_infinite_type(subs, problems, symbol, loc_var);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
new_state
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -193,3 +364,193 @@ fn check_for_infinite_type(
|
||||||
problems.push(problem);
|
problems.push(problem);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn generalize(
|
||||||
|
subs: &mut Subs,
|
||||||
|
young_mark: Mark,
|
||||||
|
visit_mark: Mark,
|
||||||
|
young_rank: Rank,
|
||||||
|
pools: &mut Pools,
|
||||||
|
) {
|
||||||
|
let young_vars = pools.get(young_rank);
|
||||||
|
let rank_table = pool_to_rank_table(subs, young_mark, young_rank, young_vars);
|
||||||
|
|
||||||
|
// get the ranks right for each entry.
|
||||||
|
// start at low ranks so that we only have to pass
|
||||||
|
// over the information once.
|
||||||
|
for (index, table) in rank_table.iter().enumerate() {
|
||||||
|
for &var in table.iter() {
|
||||||
|
adjust_rank(subs, young_mark, visit_mark, Rank::from(index), var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (last_pool, all_but_last_pool) = rank_table.split_last();
|
||||||
|
|
||||||
|
// For variables that have rank lowerer than youngRank, register them in
|
||||||
|
// the appropriate old pool if they are not redundant.
|
||||||
|
for vars in all_but_last_pool {
|
||||||
|
for &var in vars {
|
||||||
|
if !subs.redundant(var) {
|
||||||
|
let rank = subs.get(var).rank;
|
||||||
|
|
||||||
|
pools.get_mut(rank).push(var);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For variables with rank youngRank
|
||||||
|
// If rank < youngRank: register in oldPool
|
||||||
|
// otherwise generalize
|
||||||
|
for &var in last_pool {
|
||||||
|
if !subs.redundant(var) {
|
||||||
|
let mut desc = subs.get(var);
|
||||||
|
|
||||||
|
if desc.rank < young_rank {
|
||||||
|
pools.get_mut(desc.rank).push(var);
|
||||||
|
} else {
|
||||||
|
desc.rank = Rank::none();
|
||||||
|
|
||||||
|
subs.set(var, desc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pool_to_rank_table(
|
||||||
|
subs: &mut Subs,
|
||||||
|
young_mark: Mark,
|
||||||
|
young_rank: Rank,
|
||||||
|
young_vars: &[Variable],
|
||||||
|
) -> Pools {
|
||||||
|
let mut pools = Pools::new(young_rank.into_usize() + 1);
|
||||||
|
|
||||||
|
// Sort the variables into buckets by rank.
|
||||||
|
for &var in young_vars.iter() {
|
||||||
|
let rank = subs.get(var).rank;
|
||||||
|
|
||||||
|
subs.set_mark(var, young_mark);
|
||||||
|
|
||||||
|
pools.get_mut(rank).push(var);
|
||||||
|
}
|
||||||
|
|
||||||
|
pools
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Adjust variable ranks such that ranks never increase as you move deeper.
|
||||||
|
/// This way the outermost rank is representative of the entire structure.
|
||||||
|
///
|
||||||
|
fn adjust_rank(
|
||||||
|
subs: &mut Subs,
|
||||||
|
young_mark: Mark,
|
||||||
|
visit_mark: Mark,
|
||||||
|
group_rank: Rank,
|
||||||
|
var: Variable,
|
||||||
|
) -> Rank {
|
||||||
|
let mut desc = subs.get(var);
|
||||||
|
let mark = desc.mark;
|
||||||
|
|
||||||
|
if mark == young_mark {
|
||||||
|
desc.mark = visit_mark;
|
||||||
|
|
||||||
|
let content = desc.content.clone();
|
||||||
|
let mut marked_desc = desc.clone();
|
||||||
|
|
||||||
|
// Mark the variable as visited before adjusting content, as it may be cyclic.
|
||||||
|
subs.set(var, desc);
|
||||||
|
|
||||||
|
let max_rank = adjust_rank_content(subs, young_mark, visit_mark, group_rank, content);
|
||||||
|
marked_desc.rank = max_rank;
|
||||||
|
|
||||||
|
subs.set(var, marked_desc);
|
||||||
|
|
||||||
|
max_rank
|
||||||
|
} else if mark == visit_mark {
|
||||||
|
desc.rank
|
||||||
|
} else {
|
||||||
|
let min_rank = desc.rank.min(group_rank);
|
||||||
|
|
||||||
|
// TODO from elm-compiler: how can min_rank ever be group_rank?
|
||||||
|
desc.rank = min_rank;
|
||||||
|
desc.mark = visit_mark;
|
||||||
|
|
||||||
|
subs.set(var, desc);
|
||||||
|
|
||||||
|
min_rank
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adjust_rank_content(
|
||||||
|
subs: &mut Subs,
|
||||||
|
young_mark: Mark,
|
||||||
|
visit_mark: Mark,
|
||||||
|
group_rank: Rank,
|
||||||
|
content: Content,
|
||||||
|
) -> Rank {
|
||||||
|
use crate::subs::Content::*;
|
||||||
|
use crate::subs::FlatType::*;
|
||||||
|
|
||||||
|
match content {
|
||||||
|
FlexVar(_) | RigidVar(_) | Error(_) => group_rank,
|
||||||
|
|
||||||
|
Structure(flat_type) => {
|
||||||
|
match flat_type {
|
||||||
|
Apply { args, .. } => {
|
||||||
|
let mut rank = Rank::outermost();
|
||||||
|
|
||||||
|
for var in args {
|
||||||
|
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
|
}
|
||||||
|
|
||||||
|
rank
|
||||||
|
}
|
||||||
|
|
||||||
|
Func(arg_vars, ret_var) => {
|
||||||
|
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, ret_var);
|
||||||
|
|
||||||
|
for var in arg_vars {
|
||||||
|
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
|
}
|
||||||
|
|
||||||
|
rank
|
||||||
|
}
|
||||||
|
|
||||||
|
EmptyRecord => {
|
||||||
|
// from elm-compiler: THEORY: an empty record never needs to get generalized
|
||||||
|
Rank::outermost()
|
||||||
|
}
|
||||||
|
|
||||||
|
Record(fields, ext_var) => {
|
||||||
|
let mut rank = adjust_rank(subs, young_mark, visit_mark, group_rank, ext_var);
|
||||||
|
|
||||||
|
for (_, var) in fields {
|
||||||
|
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
|
}
|
||||||
|
|
||||||
|
rank
|
||||||
|
}
|
||||||
|
Erroneous(_) => group_rank,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Alias(_, _, args, _) => {
|
||||||
|
let mut rank = Rank::outermost();
|
||||||
|
|
||||||
|
// from elm-compiler: THEORY: anything in the realVar would be outermostRank
|
||||||
|
for (_, var) in args {
|
||||||
|
rank = rank.max(adjust_rank(subs, young_mark, visit_mark, group_rank, var));
|
||||||
|
}
|
||||||
|
|
||||||
|
rank
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn introduce(subs: &mut Subs, rank: Rank, pools: &mut Pools, vars: &[Variable]) {
|
||||||
|
let pool: &mut Vec<Variable> = pools.get_mut(rank);
|
||||||
|
|
||||||
|
for &var in vars.iter() {
|
||||||
|
subs.set_rank(var, rank);
|
||||||
|
}
|
||||||
|
|
||||||
|
pool.extend(vars);
|
||||||
|
}
|
||||||
|
|
36
src/subs.rs
36
src/subs.rs
|
@ -11,7 +11,7 @@ pub struct Mark(i32);
|
||||||
impl Mark {
|
impl Mark {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn none() -> Mark {
|
pub fn none() -> Mark {
|
||||||
Mark(0)
|
Mark(2)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
@ -21,12 +21,12 @@ impl Mark {
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn get_var_names() -> Mark {
|
pub fn get_var_names() -> Mark {
|
||||||
Mark(2)
|
Mark(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn next(self) -> Mark {
|
pub fn next(self) -> Mark {
|
||||||
Mark(self.0 - 1)
|
Mark(self.0 + 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ impl Into<usize> for VarStore {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, PartialEq, Eq, Clone, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Variable(usize);
|
pub struct Variable(usize);
|
||||||
|
|
||||||
impl Variable {
|
impl Variable {
|
||||||
|
@ -159,6 +159,10 @@ impl Subs {
|
||||||
self.utable.probe_value(key)
|
self.utable.probe_value(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_without_compacting(&self, key: Variable) -> Descriptor {
|
||||||
|
self.utable.probe_value_without_compacting(key)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_root_key(&mut self, key: Variable) -> Variable {
|
pub fn get_root_key(&mut self, key: Variable) -> Variable {
|
||||||
self.utable.get_root_key(key)
|
self.utable.get_root_key(key)
|
||||||
}
|
}
|
||||||
|
@ -215,6 +219,10 @@ impl Subs {
|
||||||
self.utable.unioned(left, right)
|
self.utable.unioned(left, right)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn redundant(&mut self, var: Variable) -> bool {
|
||||||
|
self.utable.is_redirect(var)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn occurs(&mut self, var: Variable) -> bool {
|
pub fn occurs(&mut self, var: Variable) -> bool {
|
||||||
occurs(self, &ImSet::default(), var)
|
occurs(self, &ImSet::default(), var)
|
||||||
}
|
}
|
||||||
|
@ -244,7 +252,7 @@ fn unnamed_flex_var() -> Content {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
pub struct Rank(u8);
|
pub struct Rank(usize);
|
||||||
|
|
||||||
impl Rank {
|
impl Rank {
|
||||||
pub fn none() -> Self {
|
pub fn none() -> Self {
|
||||||
|
@ -258,17 +266,27 @@ impl Rank {
|
||||||
pub fn next(self) -> Self {
|
pub fn next(self) -> Self {
|
||||||
Rank(self.0 + 1)
|
Rank(self.0 + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn into_usize(self) -> usize {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<u8> for Rank {
|
impl fmt::Display for Rank {
|
||||||
fn into(self) -> u8 {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
self.0
|
self.0.fmt(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<usize> for Rank {
|
impl Into<usize> for Rank {
|
||||||
fn into(self) -> usize {
|
fn into(self) -> usize {
|
||||||
self.0 as usize
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<usize> for Rank {
|
||||||
|
fn from(index: usize) -> Self {
|
||||||
|
Rank(index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
125
src/unify.rs
125
src/unify.rs
|
@ -18,9 +18,25 @@ struct RecordStructure {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type Problems = Vec<Problem>;
|
pub type Problems = Vec<Problem>;
|
||||||
|
pub type Pool = Vec<Variable>;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn unify(subs: &mut Subs, problems: &mut Problems, var1: Variable, var2: Variable) {
|
pub fn unify(subs: &mut Subs, problems: &mut Problems, var1: Variable, var2: Variable) -> Pool {
|
||||||
|
let mut pool = Vec::new();
|
||||||
|
|
||||||
|
unify_pool(subs, &mut pool, problems, var1, var2);
|
||||||
|
|
||||||
|
pool
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn unify_pool(
|
||||||
|
subs: &mut Subs,
|
||||||
|
pool: &mut Pool,
|
||||||
|
problems: &mut Problems,
|
||||||
|
var1: Variable,
|
||||||
|
var2: Variable,
|
||||||
|
) {
|
||||||
if !subs.equivalent(var1, var2) {
|
if !subs.equivalent(var1, var2) {
|
||||||
let ctx = Context {
|
let ctx = Context {
|
||||||
first: var1,
|
first: var1,
|
||||||
|
@ -29,19 +45,24 @@ pub fn unify(subs: &mut Subs, problems: &mut Problems, var1: Variable, var2: Var
|
||||||
second_desc: subs.get(var2),
|
second_desc: subs.get(var2),
|
||||||
};
|
};
|
||||||
|
|
||||||
unify_context(subs, problems, ctx)
|
unify_context(subs, pool, problems, ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify_context(subs: &mut Subs, problems: &mut Problems, ctx: Context) {
|
fn unify_context(subs: &mut Subs, pool: &mut Pool, problems: &mut Problems, ctx: Context) {
|
||||||
match &ctx.first_desc.content {
|
match &ctx.first_desc.content {
|
||||||
FlexVar(opt_name) => unify_flex(subs, problems, &ctx, opt_name, &ctx.second_desc.content),
|
FlexVar(opt_name) => unify_flex(subs, problems, &ctx, opt_name, &ctx.second_desc.content),
|
||||||
RigidVar(name) => unify_rigid(subs, problems, &ctx, name, &ctx.second_desc.content),
|
RigidVar(name) => unify_rigid(subs, problems, &ctx, name, &ctx.second_desc.content),
|
||||||
Structure(flat_type) => {
|
Structure(flat_type) => unify_structure(
|
||||||
unify_structure(subs, problems, &ctx, flat_type, &ctx.second_desc.content)
|
subs,
|
||||||
}
|
pool,
|
||||||
|
problems,
|
||||||
|
&ctx,
|
||||||
|
flat_type,
|
||||||
|
&ctx.second_desc.content,
|
||||||
|
),
|
||||||
Alias(home, name, args, real_var) => {
|
Alias(home, name, args, real_var) => {
|
||||||
unify_alias(subs, problems, &ctx, home, name, args, *real_var)
|
unify_alias(subs, pool, problems, &ctx, home, name, args, *real_var)
|
||||||
}
|
}
|
||||||
Error(problem) => {
|
Error(problem) => {
|
||||||
// Error propagates. Whatever we're comparing it to doesn't matter!
|
// Error propagates. Whatever we're comparing it to doesn't matter!
|
||||||
|
@ -51,9 +72,12 @@ fn unify_context(subs: &mut Subs, problems: &mut Problems, ctx: Context) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO trim down this arg list
|
||||||
|
#[allow(clippy::too_many_arguments)]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_alias(
|
fn unify_alias(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
pool: &mut Pool,
|
||||||
problems: &mut Problems,
|
problems: &mut Problems,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
home: &ModuleName,
|
home: &ModuleName,
|
||||||
|
@ -72,7 +96,7 @@ fn unify_alias(
|
||||||
Alias(home.clone(), name.clone(), args.to_owned(), real_var),
|
Alias(home.clone(), name.clone(), args.to_owned(), real_var),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
RigidVar(_) => unify(subs, problems, real_var, ctx.second),
|
RigidVar(_) => unify_pool(subs, pool, problems, real_var, ctx.second),
|
||||||
Alias(other_home, other_name, other_args, other_real_var) => {
|
Alias(other_home, other_name, other_args, other_real_var) => {
|
||||||
if name == other_name && home == other_home {
|
if name == other_name && home == other_home {
|
||||||
match args.len().cmp(&other_args.len()) {
|
match args.len().cmp(&other_args.len()) {
|
||||||
|
@ -90,16 +114,16 @@ fn unify_alias(
|
||||||
}
|
}
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
for ((_, l_var), (_, r_var)) in args.iter().zip(other_args.iter()) {
|
for ((_, l_var), (_, r_var)) in args.iter().zip(other_args.iter()) {
|
||||||
unify(subs, problems, *l_var, *r_var);
|
unify_pool(subs, pool, problems, *l_var, *r_var);
|
||||||
}
|
}
|
||||||
merge(subs, &ctx, other_content.clone());
|
merge(subs, &ctx, other_content.clone());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
unify(subs, problems, real_var, *other_real_var)
|
unify_pool(subs, pool, problems, real_var, *other_real_var)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Structure(_) => unify(subs, problems, real_var, ctx.second),
|
Structure(_) => unify_pool(subs, pool, problems, real_var, ctx.second),
|
||||||
Error(problem) => {
|
Error(problem) => {
|
||||||
merge(subs, ctx, Error(problem.clone()));
|
merge(subs, ctx, Error(problem.clone()));
|
||||||
problems.push(problem.clone());
|
problems.push(problem.clone());
|
||||||
|
@ -110,6 +134,7 @@ fn unify_alias(
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_structure(
|
fn unify_structure(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
pool: &mut Pool,
|
||||||
problems: &mut Problems,
|
problems: &mut Problems,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
flat_type: &FlatType,
|
flat_type: &FlatType,
|
||||||
|
@ -128,9 +153,9 @@ fn unify_structure(
|
||||||
}
|
}
|
||||||
Structure(ref other_flat_type) => {
|
Structure(ref other_flat_type) => {
|
||||||
// Unify the two flat types
|
// Unify the two flat types
|
||||||
unify_flat_type(subs, problems, ctx, flat_type, other_flat_type)
|
unify_flat_type(subs, pool, problems, ctx, flat_type, other_flat_type)
|
||||||
}
|
}
|
||||||
Alias(_, _, _, real_var) => unify(subs, problems, ctx.first, *real_var),
|
Alias(_, _, _, real_var) => unify_pool(subs, pool, problems, ctx.first, *real_var),
|
||||||
Error(problem) => {
|
Error(problem) => {
|
||||||
// Error propagates.
|
// Error propagates.
|
||||||
merge(subs, ctx, Error(problem.clone()));
|
merge(subs, ctx, Error(problem.clone()));
|
||||||
|
@ -141,6 +166,7 @@ fn unify_structure(
|
||||||
|
|
||||||
fn unify_record(
|
fn unify_record(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
pool: &mut Pool,
|
||||||
problems: &mut Problems,
|
problems: &mut Problems,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
rec1: RecordStructure,
|
rec1: RecordStructure,
|
||||||
|
@ -156,9 +182,10 @@ fn unify_record(
|
||||||
|
|
||||||
if unique_fields1.is_empty() {
|
if unique_fields1.is_empty() {
|
||||||
if unique_fields2.is_empty() {
|
if unique_fields2.is_empty() {
|
||||||
unify(subs, problems, rec1.ext, rec2.ext);
|
unify_pool(subs, pool, problems, rec1.ext, rec2.ext);
|
||||||
unify_shared_fields(
|
unify_shared_fields(
|
||||||
subs,
|
subs,
|
||||||
|
pool,
|
||||||
problems,
|
problems,
|
||||||
ctx,
|
ctx,
|
||||||
shared_fields,
|
shared_fields,
|
||||||
|
@ -167,12 +194,13 @@ fn unify_record(
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let flat_type = FlatType::Record(unique_fields2, rec2.ext);
|
let flat_type = FlatType::Record(unique_fields2, rec2.ext);
|
||||||
let sub_record = subs.fresh(Structure(flat_type).into());
|
let sub_record = fresh(subs, pool, ctx, Structure(flat_type));
|
||||||
|
|
||||||
unify(subs, problems, rec1.ext, sub_record);
|
unify_pool(subs, pool, problems, rec1.ext, sub_record);
|
||||||
|
|
||||||
unify_shared_fields(
|
unify_shared_fields(
|
||||||
subs,
|
subs,
|
||||||
|
pool,
|
||||||
problems,
|
problems,
|
||||||
ctx,
|
ctx,
|
||||||
shared_fields,
|
shared_fields,
|
||||||
|
@ -182,12 +210,13 @@ fn unify_record(
|
||||||
}
|
}
|
||||||
} else if unique_fields2.is_empty() {
|
} else if unique_fields2.is_empty() {
|
||||||
let flat_type = FlatType::Record(unique_fields1, rec1.ext);
|
let flat_type = FlatType::Record(unique_fields1, rec1.ext);
|
||||||
let sub_record = subs.fresh(Structure(flat_type).into());
|
let sub_record = fresh(subs, pool, ctx, Structure(flat_type));
|
||||||
|
|
||||||
unify(subs, problems, sub_record, rec2.ext);
|
unify_pool(subs, pool, problems, sub_record, rec2.ext);
|
||||||
|
|
||||||
unify_shared_fields(
|
unify_shared_fields(
|
||||||
subs,
|
subs,
|
||||||
|
pool,
|
||||||
problems,
|
problems,
|
||||||
ctx,
|
ctx,
|
||||||
shared_fields,
|
shared_fields,
|
||||||
|
@ -196,21 +225,22 @@ fn unify_record(
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
let other_fields = unique_fields1.clone().union(unique_fields2.clone());
|
let other_fields = unique_fields1.clone().union(unique_fields2.clone());
|
||||||
let ext = subs.fresh_unnamed_flex_var();
|
let ext = fresh(subs, pool, ctx, Content::FlexVar(None));
|
||||||
let flat_type1 = FlatType::Record(unique_fields1, rec1.ext);
|
let flat_type1 = FlatType::Record(unique_fields1, rec1.ext);
|
||||||
let sub1 = subs.fresh(Structure(flat_type1).into());
|
let sub1 = fresh(subs, pool, ctx, Structure(flat_type1));
|
||||||
let flat_type2 = FlatType::Record(unique_fields2, rec2.ext);
|
let flat_type2 = FlatType::Record(unique_fields2, rec2.ext);
|
||||||
let sub2 = subs.fresh(Structure(flat_type2).into());
|
let sub2 = fresh(subs, pool, ctx, Structure(flat_type2));
|
||||||
|
|
||||||
unify(subs, problems, rec1.ext, sub2);
|
unify_pool(subs, pool, problems, rec1.ext, sub2);
|
||||||
unify(subs, problems, sub1, rec2.ext);
|
unify_pool(subs, pool, problems, sub1, rec2.ext);
|
||||||
|
|
||||||
unify_shared_fields(subs, problems, ctx, shared_fields, other_fields, ext);
|
unify_shared_fields(subs, pool, problems, ctx, shared_fields, other_fields, ext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify_shared_fields(
|
fn unify_shared_fields(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
pool: &mut Pool,
|
||||||
problems: &mut Problems,
|
problems: &mut Problems,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
shared_fields: ImMap<Lowercase, (Variable, Variable)>,
|
shared_fields: ImMap<Lowercase, (Variable, Variable)>,
|
||||||
|
@ -225,7 +255,7 @@ fn unify_shared_fields(
|
||||||
|
|
||||||
// TODO another way to do this might be to pass around a problems vec
|
// TODO another way to do this might be to pass around a problems vec
|
||||||
// and check to see if its length increased after doing this unification.
|
// and check to see if its length increased after doing this unification.
|
||||||
unify(subs, problems, actual, expected);
|
unify_pool(subs, pool, problems, actual, expected);
|
||||||
|
|
||||||
if problems.len() == prev_problem_count {
|
if problems.len() == prev_problem_count {
|
||||||
matching_fields.insert(name, actual);
|
matching_fields.insert(name, actual);
|
||||||
|
@ -248,6 +278,7 @@ fn unify_shared_fields(
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn unify_flat_type(
|
fn unify_flat_type(
|
||||||
subs: &mut Subs,
|
subs: &mut Subs,
|
||||||
|
pool: &mut Pool,
|
||||||
problems: &mut Problems,
|
problems: &mut Problems,
|
||||||
ctx: &Context,
|
ctx: &Context,
|
||||||
left: &FlatType,
|
left: &FlatType,
|
||||||
|
@ -261,18 +292,18 @@ fn unify_flat_type(
|
||||||
}
|
}
|
||||||
|
|
||||||
(Record(fields, ext), EmptyRecord) if fields.is_empty() => {
|
(Record(fields, ext), EmptyRecord) if fields.is_empty() => {
|
||||||
unify(subs, problems, *ext, ctx.second)
|
unify_pool(subs, pool, problems, *ext, ctx.second)
|
||||||
}
|
}
|
||||||
|
|
||||||
(EmptyRecord, Record(fields, ext)) if fields.is_empty() => {
|
(EmptyRecord, Record(fields, ext)) if fields.is_empty() => {
|
||||||
unify(subs, problems, ctx.first, *ext)
|
unify_pool(subs, pool, problems, ctx.first, *ext)
|
||||||
}
|
}
|
||||||
|
|
||||||
(Record(fields1, ext1), Record(fields2, ext2)) => {
|
(Record(fields1, ext1), Record(fields2, ext2)) => {
|
||||||
let rec1 = gather_fields(subs, problems, fields1.clone(), *ext1);
|
let rec1 = gather_fields(subs, problems, fields1.clone(), *ext1);
|
||||||
let rec2 = gather_fields(subs, problems, fields2.clone(), *ext2);
|
let rec2 = gather_fields(subs, problems, fields2.clone(), *ext2);
|
||||||
|
|
||||||
unify_record(subs, problems, ctx, rec1, rec2)
|
unify_record(subs, pool, problems, ctx, rec1, rec2)
|
||||||
}
|
}
|
||||||
(
|
(
|
||||||
Apply {
|
Apply {
|
||||||
|
@ -286,7 +317,7 @@ fn unify_flat_type(
|
||||||
args: r_args,
|
args: r_args,
|
||||||
},
|
},
|
||||||
) if l_module_name == r_module_name && l_type_name == r_type_name => {
|
) if l_module_name == r_module_name && l_type_name == r_type_name => {
|
||||||
unify_zip(subs, problems, l_args.iter(), r_args.iter());
|
unify_zip(subs, pool, problems, l_args.iter(), r_args.iter());
|
||||||
|
|
||||||
merge(
|
merge(
|
||||||
subs,
|
subs,
|
||||||
|
@ -302,8 +333,8 @@ fn unify_flat_type(
|
||||||
Ordering::Greater => merge(subs, ctx, Error(Problem::ExtraArguments)),
|
Ordering::Greater => merge(subs, ctx, Error(Problem::ExtraArguments)),
|
||||||
Ordering::Less => merge(subs, ctx, Error(Problem::MissingArguments)),
|
Ordering::Less => merge(subs, ctx, Error(Problem::MissingArguments)),
|
||||||
Ordering::Equal => {
|
Ordering::Equal => {
|
||||||
unify_zip(subs, problems, l_args.iter(), r_args.iter());
|
unify_zip(subs, pool, problems, l_args.iter(), r_args.iter());
|
||||||
unify(subs, problems, *l_ret, *r_ret);
|
unify_pool(subs, pool, problems, *l_ret, *r_ret);
|
||||||
merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret)));
|
merge(subs, ctx, Structure(Func((*r_args).clone(), *r_ret)));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -316,12 +347,17 @@ fn unify_flat_type(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn unify_zip<'a, I>(subs: &mut Subs, problems: &mut Problems, left_iter: I, right_iter: I)
|
fn unify_zip<'a, I>(
|
||||||
where
|
subs: &mut Subs,
|
||||||
|
pool: &mut Pool,
|
||||||
|
problems: &mut Problems,
|
||||||
|
left_iter: I,
|
||||||
|
right_iter: I,
|
||||||
|
) where
|
||||||
I: Iterator<Item = &'a Variable>,
|
I: Iterator<Item = &'a Variable>,
|
||||||
{
|
{
|
||||||
for (&l_var, &r_var) in left_iter.zip(right_iter) {
|
for (&l_var, &r_var) in left_iter.zip(right_iter) {
|
||||||
unify(subs, problems, l_var, r_var);
|
unify_pool(subs, pool, problems, l_var, r_var);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -412,3 +448,24 @@ fn merge(subs: &mut Subs, ctx: &Context, content: Content) {
|
||||||
|
|
||||||
subs.union(ctx.first, ctx.second, desc);
|
subs.union(ctx.first, ctx.second, desc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn register(subs: &mut Subs, desc: Descriptor, pool: &mut Pool) -> Variable {
|
||||||
|
let var = subs.fresh(desc);
|
||||||
|
|
||||||
|
pool.push(var);
|
||||||
|
|
||||||
|
var
|
||||||
|
}
|
||||||
|
|
||||||
|
fn fresh(subs: &mut Subs, pool: &mut Pool, ctx: &Context, content: Content) -> Variable {
|
||||||
|
register(
|
||||||
|
subs,
|
||||||
|
Descriptor {
|
||||||
|
content,
|
||||||
|
rank: ctx.first_desc.rank.min(ctx.second_desc.rank),
|
||||||
|
mark: Mark::none(),
|
||||||
|
copy: None,
|
||||||
|
},
|
||||||
|
pool,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -509,10 +509,6 @@ mod test_infer {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO type annotations
|
|
||||||
// TODO BoundTypeVariables
|
|
||||||
// TODO conditionals
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn indirect_always() {
|
fn indirect_always() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
|
|
|
@ -527,10 +527,6 @@ mod test_infer_uniq {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO type annotations
|
|
||||||
// TODO BoundTypeVariables
|
|
||||||
// TODO conditionals
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn indirect_always() {
|
fn indirect_always() {
|
||||||
infer_eq(
|
infer_eq(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue