knave 0.11.2

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::{Lifetime, RustType, Type, TypedProgram};

pub struct OpaqueBorrows<'a> {
    program: &'a TypedProgram,
}

impl<'a> OpaqueBorrows<'a> {
    pub fn new(program: &'a TypedProgram) -> Self {
        Self { program }
    }

    pub fn holds_borrow(&self, ty: &Type) -> bool {
        match ty {
            Type::Named(binding, _) => {
                self.is_opaque(*binding) && self.program.type_holds_borrow(ty)
            }
            _ => false,
        }
    }

    pub fn holds_elided_borrow(&self, ty: &Type) -> bool {
        match ty {
            Type::Ref(Lifetime::Elided, _) | Type::Mut(Lifetime::Elided, _) => true,
            Type::Named(binding, _) if self.program.has_lifetime_parameter(*binding) => true,
            Type::Function(..) => false,
            _ => Self::parts(ty)
                .into_iter()
                .any(|part| self.holds_elided_borrow(part)),
        }
    }

    pub fn written_lifetimes(&self, ty: &Type) -> FxHashSet<BindingId> {
        let mut lifetimes = FxHashSet::default();
        self.collect_opaque(ty, &mut lifetimes);
        lifetimes
    }

    fn collect_opaque(&self, ty: &Type, lifetimes: &mut FxHashSet<BindingId>) {
        match ty {
            Type::Named(binding, arguments) if self.is_opaque(*binding) => {
                for argument in arguments {
                    Self::collect_written(argument, lifetimes);
                }
            }
            _ => {
                for part in Self::parts(ty) {
                    self.collect_opaque(part, lifetimes);
                }
            }
        }
    }

    fn collect_written(ty: &Type, lifetimes: &mut FxHashSet<BindingId>) {
        if let Some(Lifetime::Written(binding)) = ty.reference_lifetime() {
            lifetimes.insert(binding);
        }
        for part in Self::parts(ty) {
            Self::collect_written(part, lifetimes);
        }
    }

    fn is_opaque(&self, binding: BindingId) -> bool {
        self.program
            .rust_types
            .get(binding)
            .is_some_and(RustType::is_opaque)
    }

    fn parts(ty: &Type) -> Vec<&Type> {
        match ty {
            Type::Ref(_, inner) | Type::Mut(_, inner) => vec![inner.as_ref()],
            Type::Named(_, arguments) => arguments.iter().collect(),
            Type::Function(_, parameters, result) => parameters
                .iter()
                .chain(std::iter::once(result.as_ref()))
                .collect(),
            Type::Var(_) | Type::Generic(_) | Type::Primitive(_) | Type::TraitObject(_) => {
                Vec::new()
            }
        }
    }
}