vize_l3 0.620.0

L3 - the Davinci reactivity IR (codename Impeto): flat scheduled ops with explicit state and effect edges
Documentation
//! Conditional admission without revisiting any branch body.

use super::{DomBuilder, DomExpressionFacts, DomUnsupported, ValueKind};
use crate::decision::dom::vue::policy::FileReads;
use crate::decision::{DecisionBuildError, dom::control::build::ConditionalFrame};
use vize_l0::{Span, id::NodeId};
use vize_l2::{
    expr::ExprRef,
    op::{IfOp, Op},
    resolution::Usage,
};

impl<'owner, 'arena, F: DomExpressionFacts, R: FileReads<'owner, 'arena>>
    DomBuilder<'_, 'owner, 'arena, F, R>
{
    pub(super) fn prepare_conditional(
        &mut self,
        id: NodeId,
        op: &'owner Op<'arena>,
    ) -> Option<ConditionalFrame<'owner, 'arena>> {
        let Op::If(owner) = op else { return None };
        let mut admitted = self.frames.is_empty() && self.root_count == 1;
        if !admitted {
            self.reject(id, owner.span, DomUnsupported::ConditionalRoot);
        }
        let shape = matches!(owner.branches.as_slice(), [first] if first.condition.is_some())
            || matches!(owner.branches.as_slice(), [first, last]
                if first.condition.is_some() && last.condition.is_none());
        if !shape {
            admitted = false;
            self.reject(id, owner.span, DomUnsupported::ConditionalShape);
        }
        let mut first_condition = ValueKind::ContextDependent;
        for (index, branch) in owner.branches.iter().enumerate() {
            if branch.region.ops.len() != 1 {
                admitted = false;
                self.reject(id, branch.span, DomUnsupported::ConditionalRoot);
            }
            if let Some(expression) = branch.condition {
                let value = self.value(id, expression);
                if value == Some(ValueKind::ContextDependent) {
                    continue;
                }
                if index == 0
                    && let Some(value) = value
                    && self.native_condition(id, owner, expression, value)
                {
                    first_condition = value;
                } else {
                    admitted = false;
                    self.reject(id, expression.span(), DomUnsupported::ConditionalCondition);
                }
            }
        }
        Some(if first_condition == ValueKind::ContextDependent {
            ConditionalFrame::new(owner, admitted)
        } else {
            ConditionalFrame::with_condition(owner, admitted, first_condition)
        })
    }

    fn native_condition(
        &self,
        id: NodeId,
        owner: &IfOp<'arena>,
        expression: ExprRef<'arena>,
        value: ValueKind,
    ) -> bool {
        use crate::decision::dom::vue::VueReadKind;
        if !R::NATIVE_SETUP
            || !matches!(value, ValueKind::FileDependent | ValueKind::LiteralConstant)
            || owner.branches.len() != 1
        {
            return false;
        }
        let Some(file) = self.file else { return false };
        let Some(record) = file.native_conditional(id).filter(|row| row.accepts(owner)) else {
            return false;
        };
        let ExprRef::Js(expression) = expression else {
            return false;
        };
        let Some(row) = self.facts.vue_expressions.get(id) else {
            return false;
        };
        let [read] = row.reads() else { return false };
        let resolution = row.resolution();
        record.input().matches_expression(expression)
            && core::ptr::eq(resolution.file(), file)
            && resolution.node() == id
            && resolution.scope() == Some(record.scope())
            && row.value() == value
            && record.binding() == Some(read.binding().id())
            && read.occurrence().usage == Usage::Read
            && matches!(read.kind(), VueReadKind::SetupConst | VueReadKind::SetupLet)
            && self.reads.classify(read.occurrence(), read.binding()) == Some(read.kind())
            && read.binding().declaration().is_some_and(|declaration| {
                declaration.scope == record.scope()
                    && declaration.is_direct_program()
                    && declaration.boolean_initializer().is_some()
            })
    }

    pub(super) fn complete_conditional(
        &mut self,
        id: NodeId,
        span: Span,
        frame: ConditionalFrame<'owner, 'arena>,
    ) -> Result<(), DecisionBuildError> {
        if let Some(conditional) = frame.finish(id)? {
            self.facts.controls.insert(id, conditional);
        } else if !self.facts.unsupported.iter().any(|row| row.node == id) {
            self.reject(id, span, DomUnsupported::ConditionalRoot);
        }
        Ok(())
    }
}