vize_l3 0.559.0

L3 - the Davinci reactivity IR (codename Impeto): flat scheduled ops with explicit state and effect edges
Documentation
use vize_l0::{Allocator, Span, String, cstr};
use vize_l3::op::{
    EdgeKind, EffectId, EffectScope, Op, OpId, OpKind, Phase, Program, Region, RegionId, StateEdge,
};
use vize_l3::verify::verify;

fn base<'a>(allocator: &'a Allocator, phase: Phase) -> Program<'a> {
    let mut program = Program::new(allocator, phase);
    program.push_region(Region::root(Span::new(0, 100)));
    program.push_op(Op::new(
        OpId::new(0),
        OpKind::If,
        RegionId::ROOT,
        Span::new(0, 100),
    ));
    program.push_region(Region::child(
        RegionId::new(1),
        RegionId::ROOT,
        OpId::new(0),
        Span::new(10, 90),
    ));
    program.push_effect(EffectScope {
        id: EffectId::new(0),
        owner: OpId::new(1),
        region: RegionId::new(1),
        span: Span::new(20, 60),
    });
    program.push_op(
        Op::new(
            OpId::new(1),
            OpKind::SetText,
            RegionId::new(1),
            Span::new(20, 30),
        )
        .with_effect(EffectId::new(0)),
    );
    program.push_op(
        Op::new(
            OpId::new(2),
            OpKind::SetProp,
            RegionId::new(1),
            Span::new(31, 40),
        )
        .with_effect(EffectId::new(0)),
    );
    program.push_edge(StateEdge::scoped(
        OpId::new(1),
        OpId::new(2),
        EdgeKind::EffectOrder,
        EffectId::new(0),
    ));
    program
}

fn messages(program: &Program<'_>) -> Vec<String> {
    verify(program)
        .into_iter()
        .map(|violation| cstr!("{violation}"))
        .collect()
}

#[test]
fn valid_program_has_no_phase_violations() {
    let arena = Allocator::default();
    assert_eq!(verify(&base(&arena, Phase::Built)), []);
    assert_eq!(verify(&base(&arena, Phase::Partitioned)), []);
    assert_eq!(verify(&base(&arena, Phase::Scheduled)), []);
}

#[test]
fn unresolved_state_edges_fail_exactly() {
    let arena = Allocator::default();
    let mut program = base(&arena, Phase::Built);
    program.push_edge(StateEdge::new(
        OpId::new(42),
        OpId::new(99),
        EdgeKind::DomOrder,
    ));

    assert_eq!(
        messages(&program),
        [
            "L3V004 @0:0 state edge source op#42 does not resolve",
            "L3V004 @0:0 state edge target op#99 does not resolve",
        ]
    );
}

#[test]
fn region_nesting_failures_name_the_parent_or_owner() {
    let arena = Allocator::default();
    let mut program = base(&arena, Phase::Built);
    program.push_region(Region::child(
        RegionId::new(2),
        RegionId::new(1),
        OpId::new(0),
        Span::new(80, 99),
    ));

    assert_eq!(
        messages(&program),
        [
            "L3V006 @80:99 region r#2 escapes parent region r#1",
            "L3V006 @80:99 region r#2 is owned by op#0, but the owner lives in r#0",
        ]
    );
}

#[test]
fn effect_scopes_must_contain_their_edges() {
    let arena = Allocator::default();
    let mut program = base(&arena, Phase::Built);
    program.push_op(Op::new(
        OpId::new(3),
        OpKind::SetHtml,
        RegionId::ROOT,
        Span::new(1, 2),
    ));
    program.push_edge(StateEdge::scoped(
        OpId::new(1),
        OpId::new(3),
        EdgeKind::EffectOrder,
        EffectId::new(0),
    ));

    assert_eq!(
        messages(&program),
        ["L3V007 @20:60 edge op#1 -> op#3 leaves effect scope fx#0"]
    );
}

#[test]
fn scheduled_phase_rejects_back_edges() {
    let arena = Allocator::default();
    let mut program = base(&arena, Phase::Scheduled);
    program.push_edge(StateEdge::new(
        OpId::new(2),
        OpId::new(1),
        EdgeKind::DomOrder,
    ));

    assert_eq!(
        messages(&program),
        ["L3V008 @0:0 scheduled edge op#2 -> op#1 points backward or to itself"]
    );
}