fieldwork-borrow 0.1.0

Field-level borrow splitting for Rust
Documentation
//! e4: external function needs &mut Stores (with recursion)

use fieldwork_borrow::with_;

struct SigStore {
    struct_defs: Vec<StructDef>,
}
struct DiagnosticStore {
    errors: Vec<String>,
}
struct ItemStore;

struct Stores<'a, 'b, 'c> {
    pub items: &'c mut ItemStore,
    pub sigs: &'a mut SigStore,
    pub diags: &'b mut DiagnosticStore,
}

struct StructDef {
    fields: Vec<StructDefField>,
}

struct StructDefField {
    kind: NameResolvedType,
}

enum NameResolvedType {
    SimpleCustom { id: usize },
    Array(Box<NameResolvedType>, usize),
    Other,
}

fn some_f(sigs: &SigStore, idx: usize) -> &StructDef {
    &sigs.struct_defs[idx]
}

fn some_f4(field: &StructDefField) -> &NameResolvedType {
    &field.kind
}

fn some_code2(_items: &ItemStore, id: usize, diags: &mut DiagnosticStore) {
    diags.errors.push(format!("checked id: {}", id));
}

// Original problem code (won't compile):
// fn problem(stores: &mut Stores) {
//     let struct_def = some_f(&stores.sigs, 0);
//     for field in &struct_def.fields {
//         check_invalid_cyclic_refs_in_field_kind(stores, some_f4(field));
//     }
// }

// Since the original function is recursive, we need to handle it differently.
// For non-recursive case, with_! works directly.

fn problem_non_recursive(stores: &mut Stores) {
    with_!(stores, {
        methods {
            check_field(items, mut diags, field_kind: &NameResolvedType) {
                match field_kind {
                    NameResolvedType::SimpleCustom { id, .. } => {
                        some_code2(items, *id, diags);
                    }
                    _ => {}
                }
            }
        }
        fields { sigs }
        body {
            let struct_def = some_f(sigs, 0);
            for field in &struct_def.fields {
                stores.check_field(some_f4(field));
            }
        }
    });
}

// For recursive case, we need to refactor the recursive function to take split fields
fn check_recursive(items: &ItemStore, diags: &mut DiagnosticStore, field_kind: &NameResolvedType) {
    match field_kind {
        NameResolvedType::SimpleCustom { id, .. } => {
            some_code2(items, *id, diags);
        }
        NameResolvedType::Array(inner, _) => {
            let mut current = inner;
            while let NameResolvedType::Array(next, _) = &**current {
                current = next;
            }
            // Recursive call with split fields
            check_recursive(items, diags, current);
        }
        _ => {}
    }
}

fn problem_recursive(stores: &mut Stores) {
    with_!(stores, {
        fields {
            sigs
            items
            mut diags
        }
        body {
            let struct_def = some_f(sigs, 0);
            for field in &struct_def.fields {
                check_recursive(items, diags, some_f4(field));
            }
        }
    });
}

#[test]
fn test_e4_non_recursive() {
    let mut sigs = SigStore {
        struct_defs: vec![StructDef {
            fields: vec![
                StructDefField { kind: NameResolvedType::SimpleCustom { id: 1 } },
                StructDefField { kind: NameResolvedType::SimpleCustom { id: 2 } },
            ],
        }],
    };
    let mut diags = DiagnosticStore { errors: vec![] };
    let mut items = ItemStore;

    let mut stores = Stores {
        items: &mut items,
        sigs: &mut sigs,
        diags: &mut diags,
    };

    problem_non_recursive(&mut stores);

    assert_eq!(stores.diags.errors, vec!["checked id: 1", "checked id: 2"]);
}

#[test]
fn test_e4_recursive() {
    let mut sigs = SigStore {
        struct_defs: vec![StructDef {
            fields: vec![
                StructDefField { kind: NameResolvedType::SimpleCustom { id: 1 } },
                StructDefField {
                    kind: NameResolvedType::Array(
                        Box::new(NameResolvedType::Array(
                            Box::new(NameResolvedType::SimpleCustom { id: 99 }),
                            10,
                        )),
                        5,
                    ),
                },
            ],
        }],
    };
    let mut diags = DiagnosticStore { errors: vec![] };
    let mut items = ItemStore;

    let mut stores = Stores {
        items: &mut items,
        sigs: &mut sigs,
        diags: &mut diags,
    };

    problem_recursive(&mut stores);

    assert_eq!(stores.diags.errors, vec!["checked id: 1", "checked id: 99"]);
}