knave 0.14.16

A Lisp programming language for humans and agents that infers types and ownership and compiles to readable Rust
Documentation
use super::held_borrows::HeldBorrows;
use super::problems::EmitProblems;
use crate::core::BindingId;
use crate::diagnostic::Diagnostic;
use crate::types::{CopyRules, ParameterCopy, Type, TypedBinder, TypedLambda, TypedProgram};
use rustc_hash::FxHashSet;

pub struct UnlentParameters<'a> {
    program: &'a TypedProgram,
    lambda: &'a TypedLambda,
    unlent: Vec<BindingId>,
}

impl<'a> UnlentParameters<'a> {
    pub fn of(
        program: &'a TypedProgram,
        lambda: &'a TypedLambda,
        shared_flows: impl FnOnce() -> FxHashSet<BindingId>,
    ) -> Self {
        let mut parameters = Self {
            program,
            lambda,
            unlent: Vec::new(),
        };
        parameters.unlent = parameters.changed_parameters(shared_flows);
        parameters
    }

    pub fn is_empty(&self) -> bool {
        self.unlent.is_empty()
    }

    pub fn lends(&self, binding: BindingId) -> bool {
        !self.unlent.contains(&binding)
    }

    pub fn uncapturable(&self) -> Option<BindingId> {
        self.lambda
            .params
            .iter()
            .find(|param| !self.lends(param.binding) && Self::infers_mut(param))
            .map(|param| param.binding)
    }

    pub fn uncapturable_problem(&self, binding: BindingId) -> Diagnostic {
        let param = self.parameter(binding);
        let name = self.program.bindings.get(binding).name();
        let copy = ParameterCopy::of(name, param, &self.copy_rules());
        EmitProblems::uncapturable_changed_parameter(copy, param.span)
    }

    fn infers_mut(param: &TypedBinder) -> bool {
        !param.annotated
    }

    fn changed_parameters(
        &self,
        shared_flows: impl FnOnce() -> FxHashSet<BindingId>,
    ) -> Vec<BindingId> {
        let changed: Vec<&TypedBinder> = self
            .lambda
            .params
            .iter()
            .filter(|param| self.program.parameter_modes.changes(param.binding))
            .filter(|param| self.result_cannot_hold(param))
            .collect();
        if changed.is_empty() {
            return Vec::new();
        }
        let held = HeldBorrows::new(self.program).kept_parameters(self.lambda);
        let flowing = shared_flows();
        changed
            .into_iter()
            .map(|param| param.binding)
            .filter(|binding| !held.contains(binding) && !flowing.contains(binding))
            .collect()
    }

    fn result_cannot_hold(&self, param: &TypedBinder) -> bool {
        let returns_closure = self
            .lambda
            .body
            .tail()
            .is_some_and(|tail| matches!(tail.ty, Type::Function(..)));
        returns_closure || self.copy_rules().is_copy(param.ty.referent())
    }

    fn copy_rules(&self) -> CopyRules<'_> {
        CopyRules::new(
            &self.program.traits,
            &self.program.generics,
            &self.program.structs,
            &self.program.enums,
        )
    }

    fn parameter(&self, binding: BindingId) -> &TypedBinder {
        self.lambda
            .params
            .iter()
            .find(|param| param.binding == binding)
            .expect("the binding names a parameter")
    }
}