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 super::plan::EmitPlan;
use super::types::RustTypes;
use crate::core::{BindingId, PrimitiveType};
use crate::types::{
    GenericBinding, GenericId, Type, TypedDefinition, TypedExpr, TypedExprKind, TypedLeaf,
    TypedProgram,
};

pub struct UnsizedParameters {
    generic_ids: FxHashSet<GenericId>,
}

impl UnsizedParameters {
    pub fn of(program: &TypedProgram, plan: &EmitPlan<'_>) -> Self {
        let mut instances = Vec::new();
        for module in &program.modules {
            for definition in &module.definitions {
                if plan.item(definition.binding).is_none() {
                    continue;
                }
                Self::definition(plan, definition, &mut instances);
            }
        }
        Self {
            generic_ids: Self::solve(program, instances),
        }
    }

    pub fn contains(&self, generic: GenericId) -> bool {
        self.generic_ids.contains(&generic)
    }

    fn definition(
        plan: &EmitPlan<'_>,
        definition: &TypedDefinition,
        instances: &mut Vec<(GenericId, Type)>,
    ) {
        if matches!(definition.value.kind, TypedExprKind::Fn(_)) {
            Self::expr(plan, &definition.value, instances);
        }
    }

    fn expr(plan: &EmitPlan<'_>, expr: &TypedExpr, instances: &mut Vec<(GenericId, Type)>) {
        if let TypedExprKind::Leaf(TypedLeaf::Global(binding)) = &expr.kind {
            Self::instance(plan, *binding, &expr.ty, instances);
        }
        for child in expr.children() {
            Self::expr(plan, child, instances);
        }
    }

    fn instance(
        plan: &EmitPlan<'_>,
        binding: BindingId,
        actual: &Type,
        instances: &mut Vec<(GenericId, Type)>,
    ) {
        let Some(definition) = plan.definition(binding) else {
            return;
        };
        if !matches!(definition.value.kind, TypedExprKind::Fn(_)) {
            return;
        }
        instances.extend(GenericBinding::of(&definition.scheme.body, actual).into_bound());
    }

    fn solve(program: &TypedProgram, instances: Vec<(GenericId, Type)>) -> FxHashSet<GenericId> {
        let mut found = FxHashSet::default();
        loop {
            let mut changed = false;
            for (generic, actual) in &instances {
                if Self::is_unsized(program, actual, &found) && found.insert(*generic) {
                    changed = true;
                }
            }
            if !changed {
                return found;
            }
        }
    }

    fn is_unsized(program: &TypedProgram, ty: &Type, found: &FxHashSet<GenericId>) -> bool {
        match ty {
            Type::Primitive(PrimitiveType::Str) => true,
            Type::Named(binding, _) => {
                RustTypes::is_rust_type(program, *binding, RustTypes::SLICE_TYPE)
            }
            Type::Generic(generic) => found.contains(generic),
            _ => false,
        }
    }
}