knave 0.9.16

A small Lisp for humans and agents that infers types and ownership and compiles to readable Rust
Documentation
use rustc_hash::FxHashSet;

use crate::core::BindingId;
use crate::types::{TypedExpr, TypedExprKind, TypedLeaf, TypedProgram};

pub struct EscapingLambdas {
    let_bound_lambdas: FxHashSet<BindingId>,
    escaping: FxHashSet<BindingId>,
}

impl EscapingLambdas {
    pub fn in_program(program: &TypedProgram) -> FxHashSet<BindingId> {
        let definitions = || {
            program
                .modules
                .iter()
                .flat_map(|module| &module.definitions)
        };
        let mut found = Self {
            let_bound_lambdas: definitions()
                .flat_map(|definition| definition.value.let_bindings())
                .filter(|(_, value)| value.lambda_value().is_some())
                .map(|(binder, _)| binder.binding)
                .collect(),
            escaping: FxHashSet::default(),
        };
        if !found.let_bound_lambdas.is_empty() {
            definitions().for_each(|definition| found.expr(&definition.value));
        }
        found.escaping
    }

    fn expr(&mut self, expr: &TypedExpr) {
        match &expr.kind {
            TypedExprKind::Leaf(TypedLeaf::Local(binding)) => self.use_as_value(*binding),
            TypedExprKind::Call(callee, arguments) if Self::is_local(callee) => arguments
                .iter()
                .for_each(|argument| self.expr(&argument.value)),
            _ => expr
                .children()
                .into_iter()
                .for_each(|child| self.expr(child)),
        }
    }

    fn use_as_value(&mut self, binding: BindingId) {
        if self.let_bound_lambdas.contains(&binding) {
            self.escaping.insert(binding);
        }
    }

    fn is_local(callee: &TypedExpr) -> bool {
        matches!(callee.kind, TypedExprKind::Leaf(TypedLeaf::Local(_)))
    }
}