compiler: Retain conditional explicit argument loans

f848c85f82fbaecb9603428e72db87728213057aa4bd859aa13242b0ebddb0b8
Alexis Sellier committed ago 1 parent e2a0b14e
lib/std/lang/resolver.rad +18 -0
10765 10765
fn createsExplicitBorrow(node: *ast::Node) -> bool {
10766 10766
    match node.value {
10767 10767
        case ast::NodeValue::AddressOf(_) => return true,
10768 10768
        case ast::NodeValue::As(cast) => return createsExplicitBorrow(cast.value),
10769 10769
        case ast::NodeValue::RegionApply { value, .. } => return createsExplicitBorrow(value),
10770 +
        case ast::NodeValue::CondExpr(cond) =>
10771 +
            return createsExplicitBorrow(cond.thenExpr) or createsExplicitBorrow(cond.elseExpr),
10770 10772
        else => return false,
10771 10773
    }
10772 10774
}
10773 10775
10774 10776
/// Protect explicit address arguments until their call begins.
10775 10777
unsafe fn retainCallLoan 'arena 'checking (
10776 10778
    checker: &mut LinearChecker 'arena 'checking, node: *ast::Node, exclusive: bool
10777 10779
) throws (ResolveError) where 'arena: 'checking {
10780 +
    match node.value {
10781 +
        case ast::NodeValue::CondExpr(cond) => {
10782 +
            try retainCallLoan(checker, cond.thenExpr, exclusive);
10783 +
            try retainCallLoan(checker, cond.elseExpr, exclusive);
10784 +
            return;
10785 +
        }
10786 +
        case ast::NodeValue::As(cast) => {
10787 +
            try retainCallLoan(checker, cast.value, exclusive);
10788 +
            return;
10789 +
        }
10790 +
        case ast::NodeValue::RegionApply { value, .. } => {
10791 +
            try retainCallLoan(checker, value, exclusive);
10792 +
            return;
10793 +
        }
10794 +
        else => {}
10795 +
    }
10778 10796
    let place = borrowPlace(checker.resolver, node);
10779 10797
    if place.root == nil {
10780 10798
        return;
10781 10799
    }
10782 10800
    if checker.localLen >= MAX_LINEAR_BINDINGS {
lib/std/lang/resolver/tests/regions.rad +25 -0
56 56
                else throw testing::TestError::Failed;
57 57
        }
58 58
    }
59 59
}
60 60
61 +
/// Conditional explicit arguments protect every possible borrowed place.
62 +
@test unsafe fn testConditionalCallArgumentLoans() throws (testing::TestError) {
63 +
    for program in [
64 +
        "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 +
        "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)); }",
66 +
        "fn outer(p: &mut u32, q: &mut u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(&mut *p if flag else &mut *q, &mut *p); }",
67 +
        "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &u32, n: u32) {} unsafe fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(&*p if flag else &*q, inner(q)); }",
68 +
        "fn outer(p: &mut u32, q: &mut u32) {} unsafe fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(&mut *p if flag else &mut *q, &mut *q); }",
69 +
        "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) as &u32, inner(p)); }",
70 +
        "record R { n: u32 } fn (r: &R) call(n: u32) {} fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn f 'r (p: &'r mut R, q: &'r mut R, flag: bool) { (&*p if flag else &*q).call(inner(&mut q.n)); }",
71 +
    ] {
72 +
        let mut arena = super::testArena();
73 +
        let storage: 'test = &mut arena in {
74 +
            let mut res = super::testResolver(storage);
75 +
            let result = try super::resolveProgramStr(&mut res, program);
76 +
            let error = try super::expectError(&result);
77 +
            let case resolver::ErrorKind::BorrowConflict(_) = error.kind
78 +
                else throw testing::TestError::Failed;
79 +
        }
80 +
    }
81 +
}
82 +
61 83
/// Call loans allow shared reads, separate fields, and access after the call.
62 84
@test unsafe fn testRegionalCallArgumentLoanScopes() throws (testing::TestError) {
63 85
    for program in [
86 +
        "fn read(p: &u32) -> u32 { return *p; } 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, read(p)); set *p = 3; set *q = 4; }",
87 +
        "record R { a: u32, b: u32, c: u32 } fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &mut u32, n: u32) {} fn f 'r (r: &'r mut R, flag: bool) { outer(&mut r.a if flag else &mut r.b, inner(&mut r.c)); set r.a = 3; }",
88 +
        "fn outer(p: &mut u32, n: u32) {} fn f 'r (p: &'r mut u32, flag: bool) { outer(&mut *p if flag else &mut *p, 0); set *p = 3; }",
64 89
        "fn read(p: &u32) -> u32 { return *p; } fn outer(p: &u32, n: u32) {} fn f 'r (p: &'r mut u32) { outer(&*p, read(p)); set *p = 3; }",
65 90
        "record R { a: u32, b: u32 } fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &mut u32, n: u32) {} fn f 'r (r: &'r mut R) { outer(&mut r.a, inner(&mut r.b)); set r.a = 3; }",
66 91
    ] {
67 92
        let mut arena = super::testArena();
68 93
        let storage: 'test = &mut arena in {