compiler: Retain conditional explicit argument loans
f848c85f82fbaecb9603428e72db87728213057aa4bd859aa13242b0ebddb0b8
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 { |