knave 0.14.8

A Lisp programming language for humans and agents that infers types and ownership and compiles to readable Rust
Documentation
use rustc_hash::FxHashMap;

use super::ty::{GenericId, Type};

pub struct GenericBinding {
    bound: FxHashMap<GenericId, Type>,
}

impl GenericBinding {
    pub fn of(declared: &Type, used: &Type) -> Self {
        Self::of_each([declared], [used])
    }

    pub fn of_each<'t>(
        declared: impl IntoIterator<Item = &'t Type>,
        used: impl IntoIterator<Item = &'t Type>,
    ) -> Self {
        Self {
            bound: BindingWalk::of_each(declared, used).finish(),
        }
    }

    pub fn substitute(&self, declared: &[Type]) -> Vec<Type> {
        declared
            .iter()
            .map(|parameter| parameter.substitute(&self.bound))
            .collect()
    }

    pub fn into_bound(self) -> FxHashMap<GenericId, Type> {
        self.bound
    }
}

struct BindingWalk {
    exact: FxHashMap<GenericId, Type>,
    through_reference: FxHashMap<GenericId, Type>,
}

impl BindingWalk {
    fn of(declared: &Type, used: &Type) -> Self {
        Self::of_each([declared], [used])
    }

    fn of_each<'t>(
        declared: impl IntoIterator<Item = &'t Type>,
        used: impl IntoIterator<Item = &'t Type>,
    ) -> Self {
        let mut walk = Self {
            exact: FxHashMap::default(),
            through_reference: FxHashMap::default(),
        };
        walk.bind_each(declared, used);
        walk
    }

    fn bind(&mut self, declared: &Type, used: &Type) {
        match declared {
            Type::Generic(generic) => {
                self.exact.entry(*generic).or_insert_with(|| used.clone());
            }
            Type::Ref(_, inner) | Type::Mut(_, inner) => {
                self.absorb_behind_reference(Self::of(inner, used.referent()));
            }
            _ => self.bind_parts(declared, used),
        }
    }

    fn absorb_behind_reference(&mut self, binding: Self) {
        for (generic, used) in binding.finish() {
            self.through_reference.entry(generic).or_insert(used);
        }
    }

    fn finish(self) -> FxHashMap<GenericId, Type> {
        let mut bound = self.through_reference;
        bound.extend(self.exact);
        bound
    }

    fn bind_parts(&mut self, declared: &Type, used: &Type) {
        if declared.shares_head_with(used) {
            self.bind_each(declared.parts(), used.parts());
        }
    }

    fn bind_each<'t>(
        &mut self,
        declared: impl IntoIterator<Item = &'t Type>,
        used: impl IntoIterator<Item = &'t Type>,
    ) {
        for (declared, used) in declared.into_iter().zip(used) {
            self.bind(declared, used);
        }
    }
}