compiler: Preserve exclusive loans for conditional cell borrows

4af3a3b7986d92ab09941b64e0d07f995886468b21d8316da15769c96a89dad7
Alexis Sellier committed ago 1 parent 0c149827
lib/std/lang/resolver.rad +2 -0
8583 8583
fn createsCellBorrow(node: *ast::Node) -> bool {
8584 8584
    match node.value {
8585 8585
        case ast::NodeValue::AddressOf(address) => return address.kind == ast::AddressKind::Cell,
8586 8586
        case ast::NodeValue::As(expr) => return createsCellBorrow(expr.value),
8587 8587
        case ast::NodeValue::RegionApply { value, .. } => return createsCellBorrow(value),
8588 +
        case ast::NodeValue::CondExpr(cond) =>
8589 +
            return createsCellBorrow(cond.thenExpr) or createsCellBorrow(cond.elseExpr),
8588 8590
        else => return false,
8589 8591
    }
8590 8592
}
8591 8593
8592 8594
/// Find the exclusive handle that owns an addressed place.
lib/std/lang/resolver/tests/regions.rad +35 -0
56 56
                else throw testing::TestError::Failed;
57 57
        }
58 58
    }
59 59
}
60 60
61 +
/// Conditional cell creation protects each source from ordinary shared access.
62 +
@test unsafe fn testConditionalCellArgumentLoans() throws (testing::TestError) {
63 +
    for program in [
64 +
        "fn inspect(c: &cell u32, p: &u32) { set *c = 2; } fn f(p: &mut u32, q: &mut u32, flag: bool) { inspect(&cell *p if flag else &cell *q, p); }",
65 +
        "fn inspect(c: &cell u32, p: &u32) { set *c = 2; } unsafe fn f(p: &mut u32, q: &mut u32, flag: bool) { inspect(&cell *p if flag else &cell *q, q); }",
66 +
        "fn read(p: &u32) -> u32 { return *p; } fn inspect(c: &cell u32, n: u32) {} fn f(p: &mut u32, q: &mut u32, flag: bool) { inspect(&cell *p if flag else &cell *q, read(q)); }",
67 +
        "fn inspect(c: &cell u32, p: &u32) {} fn f(p: &mut u32, q: &mut u32, flag: bool) { inspect((&cell *p if flag else &cell *q) as &cell u32, p); }",
68 +
    ] {
69 +
        let mut arena = super::testArena();
70 +
        let storage: 'test = &mut arena in {
71 +
            let mut res = super::testResolver(storage);
72 +
            let result = try super::resolveProgramStr(&mut res, program);
73 +
            let error = try super::expectError(&result);
74 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind
75 +
                else throw testing::TestError::Failed;
76 +
        }
77 +
    }
78 +
}
79 +
80 +
/// Conditional cell loans allow separate storage and shared cell aliases.
81 +
@test unsafe fn testConditionalCellArgumentLoanScopes() throws (testing::TestError) {
82 +
    for program in [
83 +
        "fn inspect(c: &cell u32, p: &u32) {} fn f(p: &mut u32, q: &mut u32, r: &u32, flag: bool) { inspect(&cell *p if flag else &cell *q, r); set *p = 1; set *q = 2; }",
84 +
        "record R { a: u32, b: u32, c: u32 } fn inspect(c: &cell u32, p: &u32) {} fn f(r: &mut R, flag: bool) { inspect(&cell r.a if flag else &cell r.b, &r.c); set r.a = 1; }",
85 +
        "fn inspect(a: &cell u32, b: &cell u32) {} fn f(a: &cell u32, b: &cell u32, flag: bool) { inspect(a if flag else b, a); }",
86 +
    ] {
87 +
        let mut arena = super::testArena();
88 +
        let storage: 'test = &mut arena in {
89 +
            let mut res = super::testResolver(storage);
90 +
            let result = try super::resolveProgramStr(&mut res, program);
91 +
            try super::expectNoErrors(&result);
92 +
        }
93 +
    }
94 +
}
95 +
61 96
/// Conditional explicit arguments protect every possible borrowed place.
62 97
@test unsafe fn testConditionalCallArgumentLoans() throws (testing::TestError) {
63 98
    for program in [
64 99
        "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &u32, n: u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(&*p if flag else &*q, inner(p)); }",
65 100
        "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &u32, n: u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(&*p if flag else &*q, inner(q)); }",