yulang-runtime-refine 0.1.0

Runtime type refinement, validation, invariant checks, and hygiene printing for Yulang.
Documentation
use super::*;

impl RefineRewriter {
    pub(super) fn initial_expr_type(
        &self,
        expr: &Expr,
        expected: Option<&RuntimeType>,
    ) -> RuntimeType {
        let original_ty = substitute_hir_type(&expr.ty, &self.substitutions);
        let constructor_expected = is_expected_nullary_constructor(expr, expected);
        let local_ty = expr_var_path(expr).and_then(|path| self.locals.get(path).cloned());
        let binding_ty = expr_var_path(expr)
            .filter(|_| !constructor_expected && local_ty.is_none())
            .and_then(|path| self.binding_types.get(path).cloned());
        let original_ty = local_ty.or(binding_ty.clone()).unwrap_or(original_ty);
        refine_expr_type_from_context(
            original_ty,
            binding_ty.as_ref(),
            expected,
            constructor_expected,
            &self.substitutions,
        )
    }
}

fn is_expected_nullary_constructor(expr: &Expr, expected: Option<&RuntimeType>) -> bool {
    match (&expr.kind, expected) {
        (ExprKind::Var(path), Some(expected)) => {
            is_nullary_constructor_path_for_type(path, expected)
        }
        _ => false,
    }
}

fn expr_var_path(expr: &Expr) -> Option<&typed_ir::Path> {
    match &expr.kind {
        ExprKind::Var(path) => Some(path),
        _ => None,
    }
}

fn refine_expr_type_from_context(
    original_ty: RuntimeType,
    binding_ty: Option<&RuntimeType>,
    expected: Option<&RuntimeType>,
    constructor_expected: bool,
    substitutions: &BTreeMap<typed_ir::TypeVar, typed_ir::Type>,
) -> RuntimeType {
    if constructor_expected {
        return expected
            .map(|expected| substitute_hir_type(expected, substitutions))
            .unwrap_or(original_ty);
    }
    if binding_ty.is_some_and(hir_type_has_vars) {
        return expected
            .map(|expected| substitute_hir_type(expected, substitutions))
            .filter(|expected| !hir_type_has_vars(expected) && !hir_type_is_core_never(expected))
            .unwrap_or(original_ty);
    }
    if binding_ty.is_some() {
        return original_ty;
    }
    expected
        .map(|expected| substitute_hir_type(expected, substitutions))
        .filter(|expected| {
            !hir_type_has_vars(expected)
                && !hir_type_is_core_never(&original_ty)
                && hir_type_compatible(expected, &original_ty)
        })
        .map(|expected| refine_expected_expr_type(&expected, &original_ty))
        .unwrap_or(original_ty)
}