compiler/
lib/
examples/
std/
arch/
char/
collections/
graph/
lang/
alloc/
ast/
gen/
il/
module/
parser/
resolver/
tests/
placeholderTests.rad
1.5 KiB
regions.rad
148.1 KiB
opaqueTests.rad
6.6 KiB
printer.rad
25.5 KiB
tests.rad
410.1 KiB
scanner/
alloc.rad
7.1 KiB
ast.rad
26.9 KiB
gen.rad
513 B
il.rad
20.4 KiB
lower.rad
321.7 KiB
module.rad
17.3 KiB
package.rad
1.3 KiB
parser.rad
92.2 KiB
resolver.rad
511.1 KiB
scanner.rad
17.9 KiB
sexpr.rad
6.7 KiB
strings.rad
2.2 KiB
types.rad
1.6 KiB
sys/
arch.rad
68 B
char.rad
855 B
collections.rad
39 B
fmt.rad
8.3 KiB
graph.rad
4.3 KiB
intrinsics.rad
467 B
io.rad
1.7 KiB
lang.rad
276 B
mem.rad
2.3 KiB
sys.rad
179 B
testing.rad
2.4 KiB
tests.rad
15.7 KiB
vec.rad
3.2 KiB
std.rad
299 B
scripts/
seed/
sublime/
test/
vim/
.gitignore
336 B
.gitsigners
112 B
CELL_PERMISSIONS
6.8 KiB
CONTRIBUTING
2.1 KiB
LICENSE
1.1 KiB
Makefile
5.4 KiB
README
2.5 KiB
STYLE
2.5 KiB
std.lib
1.5 KiB
std.lib.test
808 B
lib/std/lang/resolver/tests/regions.rad
raw
| 1 | //! Lexical region resolver tests. |
| 2 | |
| 3 | use std::mem; |
| 4 | use std::testing; |
| 5 | use std::lang::types; |
| 6 | use std::lang::resolver; |
| 7 | use std::lang::ast; |
| 8 | |
| 9 | /// Call results resolve regional nominal views before type consumers inspect them. |
| 10 | @test unsafe fn testCallResultNominalViews() throws (testing::TestError) { |
| 11 | let programs = [ |
| 12 | "union R: 'r + Copy { V(&'r u32), E } record M {} fn (m: *M) get 'r(p: &'r u32) -> R 'r { return R::V(p); } fn f 'r(m: *M, p: &'r u32) { let x = m.get 'r(p); match x { case R::V(v) => {}, case R::E => {} } }", |
| 13 | "union R: 'r + Copy { V(&'r u32), E } trait T { fn (*T) get 'r(p: &'r u32) -> R 'r; } fn f 'r(t: *opaque T, p: &'r u32) { let x = t.get 'r(p); match x { case R::V(v) => {}, case R::E => {} } }", |
| 14 | "union R: 'r + Copy { V(&'r u32), E } fn get 'r(p: &'r u32) -> ?R 'r { return R::V(p); } fn f 'r(p: &'r u32) { let x = get 'r(p); if let v = x { match v { case R::V(y) => {}, case R::E => {} } } }", |
| 15 | "union R: 'r + Copy { V(&'r u32), E } fn get 'r(p: &'r u32) -> [R 'r; 1] { return [R::V(p)]; } fn f 'r(p: &'r u32) { let x = get 'r(p); match x[0] { case R::V(y) => {}, case R::E => {} } }", |
| 16 | ]; |
| 17 | for program in programs { |
| 18 | let mut arena = super::testArena(); |
| 19 | let storage: 'test = &mut arena in { |
| 20 | let mut res = super::testResolver(storage); |
| 21 | let result = try super::resolveProgramStr(&mut res, program); |
| 22 | try super::expectNoErrors(&result); |
| 23 | } |
| 24 | } |
| 25 | } |
| 26 | |
| 27 | /// Optional aggregate payloads start at their required alignment after the tag. |
| 28 | @test fn testOptionalPayloadOffsets() throws (testing::TestError) { |
| 29 | for alignment in [1 as u32, 2, 4, 8, 16] { |
| 30 | let layout = resolver::Layout { size: 32, alignment }; |
| 31 | assert resolver::getOptionalValOffset(layout) == alignment; |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | /// Each declaration has a distinct identity within its resolver. |
| 36 | @test unsafe fn testSymbolIdentities() throws (testing::TestError) { |
| 37 | let mut arena = super::testArena(); |
| 38 | let storage: 'test = &mut arena in { |
| 39 | let mut res = super::testResolver(storage); |
| 40 | let result = try super::resolveProgramStr(&mut res, "fn f() {} fn g() {}"); |
| 41 | try super::expectNoErrors(&result); |
| 42 | let case ast::NodeValue::Block(block) = result.root.value else throw testing::TestError::Failed; |
| 43 | let first = resolver::symbolFor(&res, block.statements[0]) else throw testing::TestError::Failed; |
| 44 | let second = resolver::symbolFor(&res, block.statements[1]) else throw testing::TestError::Failed; |
| 45 | assert first.id <> second.id; |
| 46 | assert first.id < res.symbolCount; |
| 47 | assert second.id < res.symbolCount; |
| 48 | let firstBinding = resolver::nodeData(&res, block.statements[0]).binding else throw testing::TestError::Failed; |
| 49 | let secondBinding = resolver::nodeData(&res, block.statements[1]).binding else throw testing::TestError::Failed; |
| 50 | assert firstBinding.id == first.id; |
| 51 | assert secondBinding.id == second.id; |
| 52 | } |
| 53 | } |
| 54 | |
| 55 | /// Trait lookup accepts empty tables from safe code. |
| 56 | @test fn testEmptyTraitMethodLookup() throws (testing::TestError) { |
| 57 | assert resolver::findTraitMethod(&[], "missing") == nil; |
| 58 | } |
| 59 | |
| 60 | /// Trait lookup returns the matching method metadata in declaration order. |
| 61 | @test unsafe fn testTraitMethodLookup() throws (testing::TestError) { |
| 62 | let mut arena = super::testArena(); |
| 63 | let storage: 'test = &mut arena in { |
| 64 | let mut res = super::testResolver(storage); |
| 65 | let result = try super::resolveProgramStr(&mut res, "trait R { fn (&R) first(); fn (&mut R) second(); }"); |
| 66 | try super::expectNoErrors(&result); |
| 67 | let case ast::NodeValue::Block(block) = result.root.value else throw testing::TestError::Failed; |
| 68 | let sym = resolver::symbolFor(&res, block.statements[0]) else throw testing::TestError::Failed; |
| 69 | let case resolver::SymbolData::Trait(info) = sym.data else throw testing::TestError::Failed; |
| 70 | let first = resolver::findTraitMethod(&info.methods[..], "first") else throw testing::TestError::Failed; |
| 71 | let last = resolver::findTraitMethod(&info.methods[..], "second") else throw testing::TestError::Failed; |
| 72 | assert first.index == 0; |
| 73 | assert not first.mutable; |
| 74 | assert last.index == 1; |
| 75 | assert last.mutable; |
| 76 | assert resolver::findTraitMethod(&info.methods[..], "missing") == nil; |
| 77 | } |
| 78 | } |
| 79 | |
| 80 | /// Standalone method identities select independent registry entries. |
| 81 | @test unsafe fn testStandaloneMethodIdentityLookup() throws (testing::TestError) { |
| 82 | let mut arena = super::testArena(); |
| 83 | let storage: 'test = &mut arena in { |
| 84 | let mut res = super::testResolver(storage); |
| 85 | let result = try super::resolveProgramStr(&mut res, "record R {} fn (r: &R) first() {} fn (r: &mut R) second() {}"); |
| 86 | try super::expectNoErrors(&result); |
| 87 | let case ast::NodeValue::Block(block) = result.root.value else throw testing::TestError::Failed; |
| 88 | let firstSymbol = resolver::symbolFor(&res, block.statements[1]) else throw testing::TestError::Failed; |
| 89 | let lastSymbol = resolver::symbolFor(&res, block.statements[2]) else throw testing::TestError::Failed; |
| 90 | let first = resolver::findMethodBySymbol(&res, firstSymbol.id) else throw testing::TestError::Failed; |
| 91 | let last = resolver::findMethodBySymbol(&res, lastSymbol.id) else throw testing::TestError::Failed; |
| 92 | assert mem::eq(first.name, "first"); |
| 93 | assert not first.mutable; |
| 94 | assert mem::eq(last.name, "second"); |
| 95 | assert last.mutable; |
| 96 | assert first.fullFnType.paramTypes.len == 1; |
| 97 | assert first.fnType.paramTypes.len == 0; |
| 98 | assert last.fullFnType.paramTypes.len == 1; |
| 99 | assert last.fnType.paramTypes.len == 0; |
| 100 | assert resolver::findMethodBySymbol(&res, res.symbolCount) == nil; |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | /// Iteration over raw slices requires permission to read their storage. |
| 105 | @test unsafe fn testRawSliceIterationRequiresUnsafe() throws (testing::TestError) { |
| 106 | for program in [ |
| 107 | "fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }", |
| 108 | "fn f(p: *unsafe mut [u32]) { for item, index in p { assert item == index; } }", |
| 109 | "record R { items: *unsafe [u32] } fn f(r: &R) { for item in r.items { assert item == 0; } }", |
| 110 | ] { |
| 111 | let mut arena = super::testArena(); |
| 112 | let storage: 'test = &mut arena in { |
| 113 | let mut res = super::testResolver(storage); |
| 114 | let result = try super::resolveProgramStr(&mut res, program); |
| 115 | try super::expectErrorKind(&result, resolver::ErrorKind::UnsafeOperation); |
| 116 | } |
| 117 | } |
| 118 | } |
| 119 | |
| 120 | /// Checked iteration is safe, and raw iteration accepts explicit permission. |
| 121 | @test unsafe fn testSliceIterationPermissions() throws (testing::TestError) { |
| 122 | for program in [ |
| 123 | "unsafe fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }", |
| 124 | "fn f(p: *unsafe [u32]) { unsafe { for item in p { assert item == 0; } } }", |
| 125 | "fn f(p: &[u32]) { for item in p { assert item == 0; } }", |
| 126 | "fn f(p: &mut [u32]) { for item, index in p { assert item == index; } }", |
| 127 | "fn f(p: [u32; 2]) { for item in p { assert item == 0; } }", |
| 128 | ] { |
| 129 | let mut arena = super::testArena(); |
| 130 | let storage: 'test = &mut arena in { |
| 131 | let mut res = super::testResolver(storage); |
| 132 | let result = try super::resolveProgramStr(&mut res, program); |
| 133 | try super::expectNoErrors(&result); |
| 134 | } |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | /// Short-circuit paths must agree on exact-use regional ownership. |
| 139 | @test unsafe fn testRegionalShortCircuitOwnership() throws (testing::TestError) { |
| 140 | for program in [ |
| 141 | "union Ticket: 'r + Once { Value(&'r u32) } fn take 'r (t: Ticket 'r) -> bool { match t { case Ticket::Value(p) => return *p == 1, } } fn f 'r (t: Ticket 'r) { let result = false and take(t); }", |
| 142 | "union Ticket: 'r + Once { Value(&'r u32) } fn take 'r (t: Ticket 'r) -> bool { match t { case Ticket::Value(p) => return *p == 1, } } fn f 'r (t: Ticket 'r) { let result = true or take(t); }", |
| 143 | "union Ticket: 'r + Once { Value(&'r u32) } fn take 'r (t: Ticket 'r) -> bool { match t { case Ticket::Value(p) => return *p == 1, } } fn f 'r (t: Ticket 'r, flag: bool) { let result = flag and take(t); }", |
| 144 | "union Ticket: 'r + Once { Value(&'r u32) } fn take 'r (t: Ticket 'r) -> bool { match t { case Ticket::Value(p) => return *p == 1, } } unsafe fn f 'r (t: Ticket 'r, flag: bool) { let result = flag or take(t); }", |
| 145 | ] { |
| 146 | let mut arena = super::testArena(); |
| 147 | let storage: 'test = &mut arena in { |
| 148 | let mut res = super::testResolver(storage); |
| 149 | let result = try super::resolveProgramStr(&mut res, program); |
| 150 | let error = try super::expectError(&result); |
| 151 | let case resolver::ErrorKind::LinearBranchMismatch(_) = error.kind |
| 152 | else throw testing::TestError::Failed; |
| 153 | } |
| 154 | } |
| 155 | } |
| 156 | |
| 157 | /// The left operand executes on every short-circuit path. |
| 158 | @test unsafe fn testRegionalShortCircuitLeftConsumption() throws (testing::TestError) { |
| 159 | for program in [ |
| 160 | "union Ticket: 'r + Once { Value(&'r u32) } fn take 'r (t: Ticket 'r) -> bool { match t { case Ticket::Value(p) => return *p == 1, } } fn f 'r (t: Ticket 'r) { let result = take(t) and false; }", |
| 161 | "union Ticket: 'r + Once { Value(&'r u32) } fn take 'r (t: Ticket 'r) -> bool { match t { case Ticket::Value(p) => return *p == 1, } } fn f 'r (t: Ticket 'r) { let result = take(t) or true; }", |
| 162 | ] { |
| 163 | let mut arena = super::testArena(); |
| 164 | let storage: 'test = &mut arena in { |
| 165 | let mut res = super::testResolver(storage); |
| 166 | let result = try super::resolveProgramStr(&mut res, program); |
| 167 | try super::expectNoErrors(&result); |
| 168 | } |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | /// Earlier reference arguments protect their storage during later arguments. |
| 173 | @test unsafe fn testRegionalCallArgumentLoans() throws (testing::TestError) { |
| 174 | for program in [ |
| 175 | "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &mut u32, n: u32) {} fn f 'r (p: &'r mut u32) { outer(&mut *p, inner(p)); }", |
| 176 | "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &u32, n: u32) {} fn f 'r (p: &'r mut u32) { outer(&*p, inner(p)); }", |
| 177 | "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &mut u32, n: u32) {} unsafe fn f 'r (p: &'r mut u32) { outer(&mut *p, inner(p)); }", |
| 178 | "record R { n: u32 } fn (r: &mut R) call(n: u32) {} fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn f 'r (r: &'r mut R) { (&mut *r).call(inner(&mut r.n)); }", |
| 179 | ] { |
| 180 | let mut arena = super::testArena(); |
| 181 | let storage: 'test = &mut arena in { |
| 182 | let mut res = super::testResolver(storage); |
| 183 | let result = try super::resolveProgramStr(&mut res, program); |
| 184 | let error = try super::expectError(&result); |
| 185 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind |
| 186 | else throw testing::TestError::Failed; |
| 187 | } |
| 188 | } |
| 189 | } |
| 190 | |
| 191 | /// Conditional cell creation protects each source from ordinary shared access. |
| 192 | @test unsafe fn testConditionalCellArgumentLoans() throws (testing::TestError) { |
| 193 | for program in [ |
| 194 | "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); }", |
| 195 | "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); }", |
| 196 | "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)); }", |
| 197 | "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); }", |
| 198 | ] { |
| 199 | let mut arena = super::testArena(); |
| 200 | let storage: 'test = &mut arena in { |
| 201 | let mut res = super::testResolver(storage); |
| 202 | let result = try super::resolveProgramStr(&mut res, program); |
| 203 | let error = try super::expectError(&result); |
| 204 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind |
| 205 | else throw testing::TestError::Failed; |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | /// Conditional cell loans allow separate storage and shared cell aliases. |
| 211 | @test unsafe fn testConditionalCellArgumentLoanScopes() throws (testing::TestError) { |
| 212 | for program in [ |
| 213 | "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; }", |
| 214 | "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; }", |
| 215 | "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); }", |
| 216 | ] { |
| 217 | let mut arena = super::testArena(); |
| 218 | let storage: 'test = &mut arena in { |
| 219 | let mut res = super::testResolver(storage); |
| 220 | let result = try super::resolveProgramStr(&mut res, program); |
| 221 | try super::expectNoErrors(&result); |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | /// Conditional explicit arguments protect every possible borrowed place. |
| 227 | @test unsafe fn testConditionalCallArgumentLoans() throws (testing::TestError) { |
| 228 | for program in [ |
| 229 | "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)); }", |
| 230 | "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)); }", |
| 231 | "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); }", |
| 232 | "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)); }", |
| 233 | "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); }", |
| 234 | "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)); }", |
| 235 | "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)); }", |
| 236 | ] { |
| 237 | let mut arena = super::testArena(); |
| 238 | let storage: 'test = &mut arena in { |
| 239 | let mut res = super::testResolver(storage); |
| 240 | let result = try super::resolveProgramStr(&mut res, program); |
| 241 | let error = try super::expectError(&result); |
| 242 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind |
| 243 | else throw testing::TestError::Failed; |
| 244 | } |
| 245 | } |
| 246 | } |
| 247 | |
| 248 | /// Conditional argument alternatives cannot overlap another exclusive argument. |
| 249 | @test unsafe fn testConditionalCallArgumentOverlap() throws (testing::TestError) { |
| 250 | for program in [ |
| 251 | "fn outer(p: &mut u32, q: &mut u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(p if flag else q, p); }", |
| 252 | "fn outer(p: &mut u32, q: &mut u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(p if flag else q, q); }", |
| 253 | "fn outer(p: &mut u32, q: &mut u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(p, p if flag else q); }", |
| 254 | "fn outer(p: &mut u32, q: &mut u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(q, p if flag else q); }", |
| 255 | "fn outer(p: &mut u32, q: &mut u32) {} unsafe fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(p if flag else q, p); }", |
| 256 | "fn outer(p: &u32, q: &mut u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(p if flag else q, q); }", |
| 257 | "fn outer(p: &mut u32, q: &mut u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer(p if flag else q, q if flag else p); }", |
| 258 | "fn outer(p: &mut u32, q: &mut u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, r: &'r mut u32, a: bool, b: bool) { outer((p if a else q) if b else r, q); }", |
| 259 | "fn outer(p: &u32, q: &mut u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, flag: bool) { outer((p if flag else q) as &'r u32, p); }", |
| 260 | "record R { n: u32 } fn (r: &mut R) call(p: &R) {} fn f 'r (p: &'r mut R, q: &'r mut R, flag: bool) { (p if flag else q).call(p); }", |
| 261 | "trait R { fn (&mut R) call(p: &opaque R); } fn f 'r (p: &'r mut opaque R, q: &'r mut opaque R, flag: bool) { (p if flag else q).call(q); }", |
| 262 | ] { |
| 263 | let mut arena = super::testArena(); |
| 264 | let storage: 'test = &mut arena in { |
| 265 | let mut res = super::testResolver(storage); |
| 266 | let result = try super::resolveProgramStr(&mut res, program); |
| 267 | let error = try super::expectError(&result); |
| 268 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind |
| 269 | else throw testing::TestError::Failed; |
| 270 | } |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | /// Conditional argument alternatives permit shared access and disjoint places. |
| 275 | @test unsafe fn testConditionalCallArgumentSeparation() throws (testing::TestError) { |
| 276 | for program in [ |
| 277 | "fn outer(p: &u32, q: &u32) {} fn f 'r (p: &'r u32, q: &'r u32, flag: bool) { outer(p if flag else q, p); }", |
| 278 | "fn outer(p: &mut u32, q: &mut u32) {} fn f 'r (p: &'r mut u32, q: &'r mut u32, r: &'r mut u32, flag: bool) { outer(p if flag else q, r); }", |
| 279 | "record R: 'r { a: &'r mut u32, b: &'r mut u32, c: &'r mut u32 } fn outer(p: &mut u32, q: &mut u32) {} fn f 'r (r: &mut R 'r, flag: bool) { outer(r.a if flag else r.b, r.c); }", |
| 280 | "record R { n: u32 } fn (r: &mut R) call(p: &R) {} fn f 'r (p: &'r mut R, q: &'r mut R, r: &'r R, flag: bool) { (p if flag else q).call(r); }", |
| 281 | ] { |
| 282 | let mut arena = super::testArena(); |
| 283 | let storage: 'test = &mut arena in { |
| 284 | let mut res = super::testResolver(storage); |
| 285 | let result = try super::resolveProgramStr(&mut res, program); |
| 286 | try super::expectNoErrors(&result); |
| 287 | } |
| 288 | } |
| 289 | } |
| 290 | |
| 291 | /// Call loans allow shared reads, separate fields, and access after the call. |
| 292 | @test unsafe fn testRegionalCallArgumentLoanScopes() throws (testing::TestError) { |
| 293 | for program in [ |
| 294 | "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; }", |
| 295 | "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; }", |
| 296 | "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; }", |
| 297 | "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; }", |
| 298 | "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; }", |
| 299 | ] { |
| 300 | let mut arena = super::testArena(); |
| 301 | let storage: 'test = &mut arena in { |
| 302 | let mut res = super::testResolver(storage); |
| 303 | let result = try super::resolveProgramStr(&mut res, program); |
| 304 | try super::expectNoErrors(&result); |
| 305 | } |
| 306 | } |
| 307 | } |
| 308 | |
| 309 | /// Equal region spellings in separate functions have distinct identities. |
| 310 | @test unsafe fn testRegionIdentities() throws (testing::TestError) { |
| 311 | let mut testArena1 = super::testArena(); |
| 312 | let testStorage1: 'test1 = &mut testArena1 in { |
| 313 | let mut res = super::testResolver(testStorage1); |
| 314 | let result = try super::resolveProgramStr(&mut res, |
| 315 | "fn first 'r (p: &'r u8) -> u8 { return *p; }\nfn second 'r (p: &'r u8) -> u8 { return *p; }" |
| 316 | ); |
| 317 | try super::expectNoErrors(&result); |
| 318 | let first = try super::getBlockStmt(result.root, 0); |
| 319 | let second = try super::getBlockStmt(result.root, 1); |
| 320 | let firstType = resolver::typeFor(&res, first) else throw testing::TestError::Failed; |
| 321 | let secondType = resolver::typeFor(&res, second) else throw testing::TestError::Failed; |
| 322 | let case resolver::Type::Fn(a) = firstType else throw testing::TestError::Failed; |
| 323 | let case resolver::Type::Fn(b) = secondType else throw testing::TestError::Failed; |
| 324 | let case resolver::Type::Pointer { class: types::PointerClass::Region(ar), .. } = *a.paramTypes[0] |
| 325 | else throw testing::TestError::Failed; |
| 326 | let case resolver::Type::Pointer { class: types::PointerClass::Region(br), .. } = *b.paramTypes[0] |
| 327 | else throw testing::TestError::Failed; |
| 328 | try testing::expect(mem::eq(ar.name, br.name)); |
| 329 | try testing::expect(ar.id <> br.id); |
| 330 | try testing::expect(not resolver::typesEqual(*a.paramTypes[0], *b.paramTypes[0])); |
| 331 | try testing::expect(resolver::getTypeLayout(*a.paramTypes[0]).size == 8); |
| 332 | } |
| 333 | } |
| 334 | |
| 335 | /// Parent links resolve forward declarations and preserve transitive ancestry. |
| 336 | @test unsafe fn testRegionParents() throws (testing::TestError) { |
| 337 | let mut testArena2 = super::testArena(); |
| 338 | let testStorage2: 'test2 = &mut testArena2 in { |
| 339 | let mut res = super::testResolver(testStorage2); |
| 340 | let result = try super::resolveProgramStr(&mut res, |
| 341 | "fn read 'child 'middle 'root (p: &'child u8, q: &'root u8) where 'middle: 'child, 'root: 'middle {}" |
| 342 | ); |
| 343 | try super::expectNoErrors(&result); |
| 344 | let func = try super::getBlockStmt(result.root, 0); |
| 345 | let ty = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
| 346 | let case resolver::Type::Fn(info) = ty else throw testing::TestError::Failed; |
| 347 | let case resolver::Type::Pointer { class: types::PointerClass::Region(child), .. } = *info.paramTypes[0] |
| 348 | else throw testing::TestError::Failed; |
| 349 | let case resolver::Type::Pointer { class: types::PointerClass::Region(root), .. } = *info.paramTypes[1] |
| 350 | else throw testing::TestError::Failed; |
| 351 | try testing::expect(types::regionContains(root, child)); |
| 352 | try testing::expect(types::regionContains(child, child)); |
| 353 | try testing::expect(not types::regionContains(child, root)); |
| 354 | } |
| 355 | } |
| 356 | |
| 357 | /// Region declarations reject duplicate names, missing parents, and cycles. |
| 358 | @test unsafe fn testInvalidRegionDeclarations() throws (testing::TestError) { |
| 359 | for program in [ |
| 360 | "fn f 'r 'r () {}", |
| 361 | "fn f 'r () where 'missing: 'r {}", |
| 362 | "fn f 'r () where 'r: 'r {}", |
| 363 | "fn f 'a 'b 'c () where 'b: 'a, 'c: 'b, 'a: 'c {}", |
| 364 | ] { |
| 365 | let mut testArena3 = super::testArena(); |
| 366 | let testStorage3: 'test3 = &mut testArena3 in { |
| 367 | let mut res = super::testResolver(testStorage3); |
| 368 | let result = try super::resolveProgramStr(&mut res, program); |
| 369 | let _ = try super::expectError(&result); |
| 370 | } |
| 371 | } |
| 372 | } |
| 373 | |
| 374 | /// Regions from another declaration cannot enter a function signature or body. |
| 375 | @test unsafe fn testUnknownRegionNames() throws (testing::TestError) { |
| 376 | for program in [ |
| 377 | "fn f(p: &'missing u8) {}", |
| 378 | "fn f 'r (p: &'r u8) {} fn g(p: &'r u8) {}", |
| 379 | "unsafe fn f(p: &'missing u8) {}", |
| 380 | "fn f 'r (p: &'r u8) { let q: &'missing u8 = p; }", |
| 381 | ] { |
| 382 | let mut testArena4 = super::testArena(); |
| 383 | let testStorage4: 'test4 = &mut testArena4 in { |
| 384 | let mut res = super::testResolver(testStorage4); |
| 385 | let result = try super::resolveProgramStr(&mut res, program); |
| 386 | let err = try super::expectError(&result); |
| 387 | let case resolver::ErrorKind::UnknownRegion(_) = err.kind |
| 388 | else throw testing::TestError::Failed; |
| 389 | } |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | /// An ancestor relation does not make distinct reference regions assignable. |
| 394 | @test unsafe fn testRegionInvariance() throws (testing::TestError) { |
| 395 | for program in [ |
| 396 | "fn f 'a 'b (p: &'a u8) where 'a: 'b { let q: &'b u8 = p; }", |
| 397 | "unsafe fn f 'a 'b (p: &'b u8) where 'a: 'b { let q: &'a u8 = p; }", |
| 398 | ] { |
| 399 | let mut testArena5 = super::testArena(); |
| 400 | let testStorage5: 'test5 = &mut testArena5 in { |
| 401 | let mut res = super::testResolver(testStorage5); |
| 402 | let result = try super::resolveProgramStr(&mut res, program); |
| 403 | let err = try super::expectError(&result); |
| 404 | let case resolver::ErrorKind::TypeMismatch(_) = err.kind |
| 405 | else throw testing::TestError::Failed; |
| 406 | } |
| 407 | } |
| 408 | } |
| 409 | |
| 410 | /// Nominal region environments diagnose their own invalid parent relations. |
| 411 | @test unsafe fn testNominalRegionNames() throws (testing::TestError) { |
| 412 | for program in [ |
| 413 | "record R: 'r where 'missing: 'r { value: u8 }", |
| 414 | "union U: 'r where 'missing: 'r { Empty }", |
| 415 | "record R: 'r { value: &'other u8 }", |
| 416 | "union U: 'r { Value(&'other u8) }", |
| 417 | ] { |
| 418 | let mut testArena6 = super::testArena(); |
| 419 | let testStorage6: 'test6 = &mut testArena6 in { |
| 420 | let mut res = super::testResolver(testStorage6); |
| 421 | let result = try super::resolveProgramStr(&mut res, program); |
| 422 | let err = try super::expectError(&result); |
| 423 | let case resolver::ErrorKind::UnknownRegion(_) = err.kind |
| 424 | else throw testing::TestError::Failed; |
| 425 | } |
| 426 | } |
| 427 | for program in [ |
| 428 | "record R: 'r + 'r { value: u8 }", |
| 429 | "union U: 'r + 'r { Empty }", |
| 430 | ] { |
| 431 | let mut testArena7 = super::testArena(); |
| 432 | let testStorage7: 'test7 = &mut testArena7 in { |
| 433 | let mut res = super::testResolver(testStorage7); |
| 434 | let result = try super::resolveProgramStr(&mut res, program); |
| 435 | let _ = try super::expectErrorKind(&result, resolver::ErrorKind::DuplicateBinding("'r")); |
| 436 | } |
| 437 | } |
| 438 | } |
| 439 | |
| 440 | /// Region declarations restore their environment after a signature error. |
| 441 | @test unsafe fn testRegionErrorScopeRestoration() throws (testing::TestError) { |
| 442 | let mut testArena8 = super::testArena(); |
| 443 | let testStorage8: 'test8 = &mut testArena8 in { |
| 444 | let mut res = super::testResolver(testStorage8); |
| 445 | let result = try super::resolveProgramStr(&mut res, |
| 446 | "fn f 'r (p: &'missing u8) {} fn g 'other (p: &'r u8) {}" |
| 447 | ); |
| 448 | let err = try super::expectError(&result); |
| 449 | let case resolver::ErrorKind::UnknownRegion(name) = err.kind |
| 450 | else throw testing::TestError::Failed; |
| 451 | try testing::expect(mem::eq(name, "'missing")); |
| 452 | try testing::expect(res.regionScope == nil); |
| 453 | try testing::expect(res.currentFn == nil); |
| 454 | try testing::expect(res.currentFnNode == nil); |
| 455 | } |
| 456 | } |
| 457 | |
| 458 | /// Regional exclusive references retain affine ownership and arithmetic checks. |
| 459 | @test unsafe fn testRegionExclusiveOwnership() throws (testing::TestError) { |
| 460 | for program in [ |
| 461 | "fn f 'r (p: &'r mut u8) { let q: &'r mut u8 = p; set *p = 3; }", |
| 462 | "unsafe fn f 'r (p: &'r mut u8) { let q: &'r mut u8 = p; set *p = 3; }", |
| 463 | "unsafe fn f 'r (p: &'r mut u8) { let q = p + 1; }", |
| 464 | "unsafe fn f 'r (p: &'r mut u8) { let q = p as *mut u8; }", |
| 465 | ] { |
| 466 | let mut testArena9 = super::testArena(); |
| 467 | let testStorage9: 'test9 = &mut testArena9 in { |
| 468 | let mut res = super::testResolver(testStorage9); |
| 469 | let result = try super::resolveProgramStr(&mut res, program); |
| 470 | let _ = try super::expectError(&result); |
| 471 | } |
| 472 | } |
| 473 | } |
| 474 | |
| 475 | /// Callback signatures preserve the same region substitution as data arguments. |
| 476 | @test unsafe fn testRegionCallbackSubstitution() throws (testing::TestError) { |
| 477 | let mut testArena10 = super::testArena(); |
| 478 | let testStorage10: 'test10 = &mut testArena10 in { |
| 479 | let mut res = super::testResolver(testStorage10); |
| 480 | let result = try super::resolveProgramStr(&mut res, "fn read 'r (p: &'r u32) -> u32 { return *p; } fn useOp 'r (p: &'r u32, op: fn(&'r u32) -> u32) -> u32 { return op(p); } fn relay 'a 'b (p: &'a u32) -> u32 { return useOp(p, read 'b); }"); |
| 481 | let err = try super::expectError(&result); |
| 482 | let case resolver::ErrorKind::RegionInference(_) = err.kind else throw testing::TestError::Failed; |
| 483 | } |
| 484 | } |
| 485 | |
| 486 | /// A single region parameter requires one consistent named argument. |
| 487 | @test unsafe fn testRegionCallInferenceFailures() throws (testing::TestError) { |
| 488 | for program in [ |
| 489 | "fn pair 'r (a: &'r u32, b: &'r u32) {} fn relay 'a 'b (a: &'a u32, b: &'b u32) { pair(a, b); }", |
| 490 | "fn read 'r (p: &'r u32) {} fn relay(p: &u32) { read(p); }", |
| 491 | "unsafe fn read 'r (p: &'r u32) {} unsafe fn relay(p: *unsafe u32) { read(p); }", |
| 492 | "fn answer 'r () -> u32 { return 42; } fn relay() -> u32 { return answer(); }", |
| 493 | "fn read 'r (p: ?&'r u32) {} fn relay() { read(nil); }", |
| 494 | "unsafe fn read 'r (p: ?&'r u32) {} unsafe fn relay() { read(nil); }", |
| 495 | ] { |
| 496 | let mut testArena11 = super::testArena(); |
| 497 | let testStorage11: 'test11 = &mut testArena11 in { |
| 498 | let mut res = super::testResolver(testStorage11); |
| 499 | let result = try super::resolveProgramStr(&mut res, program); |
| 500 | let err = try super::expectError(&result); |
| 501 | let case resolver::ErrorKind::RegionInference(_) = err.kind else throw testing::TestError::Failed; |
| 502 | } |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | /// Inferred and explicit region arguments must satisfy declared parent relations. |
| 507 | @test unsafe fn testRegionCallParents() throws (testing::TestError) { |
| 508 | for program in [ |
| 509 | "fn pair 'p 'c (p: &'p u32, c: &'c u32) where 'p: 'c {} fn relay 'a 'b (a: &'a u32, b: &'b u32) { pair(a, b); }", |
| 510 | "fn pair 'p 'c (p: &'p u32, c: &'c u32) where 'p: 'c {} unsafe fn relay 'a 'b (a: &'a u32, b: &'b u32) where 'a: 'b { pair 'b 'a (b, a); }", |
| 511 | ] { |
| 512 | let mut testArena12 = super::testArena(); |
| 513 | let testStorage12: 'test12 = &mut testArena12 in { |
| 514 | let mut res = super::testResolver(testStorage12); |
| 515 | let result = try super::resolveProgramStr(&mut res, program); |
| 516 | let err = try super::expectError(&result); |
| 517 | let case resolver::ErrorKind::RegionParent(_) = err.kind else throw testing::TestError::Failed; |
| 518 | } |
| 519 | } |
| 520 | } |
| 521 | |
| 522 | /// Region applications require the declared arity and accessible names. |
| 523 | @test unsafe fn testExplicitRegionApplicationErrors() throws (testing::TestError) { |
| 524 | for program in [ |
| 525 | "fn read 'r (p: &'r u32) {} fn relay 'a 'b (p: &'a u32) { read 'a 'b (p); }", |
| 526 | "fn read(p: &u32) {} fn relay 'a (p: &'a u32) { read 'a (p); }", |
| 527 | ] { |
| 528 | let mut testArena13 = super::testArena(); |
| 529 | let testStorage13: 'test13 = &mut testArena13 in { |
| 530 | let mut res = super::testResolver(testStorage13); |
| 531 | let result = try super::resolveProgramStr(&mut res, program); |
| 532 | let err = try super::expectError(&result); |
| 533 | let case resolver::ErrorKind::RegionArgumentCount(_) = err.kind else throw testing::TestError::Failed; |
| 534 | } |
| 535 | } |
| 536 | let mut testArena14 = super::testArena(); |
| 537 | let testStorage14: 'test14 = &mut testArena14 in { |
| 538 | let mut res = super::testResolver(testStorage14); |
| 539 | let result = try super::resolveProgramStr(&mut res, "fn read 'r (p: &'r u32) {} fn relay 'a (p: &'a u32) { read 'missing (p); }"); |
| 540 | let err = try super::expectError(&result); |
| 541 | let case resolver::ErrorKind::UnknownRegion(_) = err.kind else throw testing::TestError::Failed; |
| 542 | } |
| 543 | } |
| 544 | |
| 545 | /// Region header loans stay active through moves and nested exclusive reborrows. |
| 546 | @test unsafe fn testConcreteBorrowConflicts() throws (testing::TestError) { |
| 547 | for program in [ |
| 548 | "fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { set x = 2; } }", |
| 549 | "unsafe fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { set x = 2; } }", |
| 550 | "fn run() { let mut x: u32 = 1; let p: 'r = &mut x, q = &mut x in {} }", |
| 551 | "fn run() { let mut x: u32 = 1; let p: 'r = &x, q = &mut x in {} }", |
| 552 | "fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { { let q = p; set *q = 2; } set x = 3; } }", |
| 553 | "fn run() { let mut x: u32 = 1; let p: 'a = &mut x in { let q: 'b = &mut *p in { set *p = 3; } } }", |
| 554 | "fn run(i: u32, j: u32) { let mut x: [u32; 2] = [1, 2]; let p: 'r = &mut x[i], q = &mut x[j] in {} }", |
| 555 | ] { |
| 556 | let mut testArena15 = super::testArena(); |
| 557 | let testStorage15: 'test15 = &mut testArena15 in { |
| 558 | let mut res = super::testResolver(testStorage15); |
| 559 | let result = try super::resolveProgramStr(&mut res, program); |
| 560 | let err = try super::expectError(&result); |
| 561 | let case resolver::ErrorKind::BorrowConflict(_) = err.kind else throw testing::TestError::Failed; |
| 562 | } |
| 563 | } |
| 564 | } |
| 565 | |
| 566 | /// Borrow headers require existing places and accessible, distinct region names. |
| 567 | @test unsafe fn testConcreteRegionNamesAndSources() throws (testing::TestError) { |
| 568 | for program in [ |
| 569 | "fn run() { let p: 'r = &1 in {} }", |
| 570 | "fn value() -> u32 { return 1; } fn run() { let p: 'r = &value() in {} }", |
| 571 | "fn run() { let x: u32 = 1; let p: 'r = &x in { let q: 'r = &x in {} } }", |
| 572 | "fn run() { let x: u32 = 1; let p: 'r = &x in {} let q: &'r u32 = &x; }", |
| 573 | "fn run 'a (p: &'a mut u32) { let q: 'b = &mut *p in {} }", |
| 574 | ] { |
| 575 | let mut testArena16 = super::testArena(); |
| 576 | let testStorage16: 'test16 = &mut testArena16 in { |
| 577 | let mut res = super::testResolver(testStorage16); |
| 578 | let result = try super::resolveProgramStr(&mut res, program); |
| 579 | let _ = try super::expectError(&result); |
| 580 | } |
| 581 | } |
| 582 | |
| 583 | } |
| 584 | |
| 585 | /// A block-local value cannot supply storage for the whole enclosing region. |
| 586 | @test unsafe fn testRegionLocalStorageBoundary() throws (testing::TestError) { |
| 587 | for program in [ |
| 588 | "fn run() { let a: u32 = 1; let p: 'outer = &a in { let mut cursor = p; let b: u32 = 2; let q: 'inner = &b where 'outer: 'inner in { set cursor = q; } } }", |
| 589 | "unsafe fn run() { let a: u32 = 1; let p: 'outer = &a in { let mut cursor = p; let b: u32 = 2; let q: 'inner = &b where 'outer: 'inner in { set cursor = q; } } }", |
| 590 | "fn run() { let x: u32 = 1; let p: 'r = &x in { let y: u32 = 2; let q: &'r u32 = &y; } }", |
| 591 | "unsafe fn run() { let x: u32 = 1; let p: 'r = &x in { let y: u32 = 2; let q: &'r u32 = &y; } }", |
| 592 | "fn run() { let mut x: u32 = 1; let p: 'a = &mut x in { let q: 'b = &mut *p in { let outer: &'a mut u32 = q; } } }", |
| 593 | ] { |
| 594 | let mut testArena17 = super::testArena(); |
| 595 | let testStorage17: 'test17 = &mut testArena17 in { |
| 596 | let mut res = super::testResolver(testStorage17); |
| 597 | let result = try super::resolveProgramStr(&mut res, program); |
| 598 | let err = try super::expectError(&result); |
| 599 | let case resolver::ErrorKind::TypeMismatch(_) = err.kind else throw testing::TestError::Failed; |
| 600 | } |
| 601 | } |
| 602 | |
| 603 | } |
| 604 | |
| 605 | /// Generic calls keep the exclusive-argument overlap check after substitution. |
| 606 | @test unsafe fn testRegionCallBorrowOverlap() throws (testing::TestError) { |
| 607 | let mut testArena18 = super::testArena(); |
| 608 | let testStorage18: 'test18 = &mut testArena18 in { |
| 609 | let mut res = super::testResolver(testStorage18); |
| 610 | let result = try super::resolveProgramStr(&mut res, "fn pair 'r (a: &'r mut u32, b: &'r mut u32) {} fn run() { let mut x: u32 = 1; let p: 'r = &mut x in { pair(p, p); } }"); |
| 611 | let err = try super::expectError(&result); |
| 612 | let case resolver::ErrorKind::BorrowConflict(_) = err.kind else throw testing::TestError::Failed; |
| 613 | } |
| 614 | } |
| 615 | |
| 616 | /// Ownership markers do not change the positions of nominal region arguments. |
| 617 | @test unsafe fn testNominalRegionParameterOrder() throws (testing::TestError) { |
| 618 | for program in [ |
| 619 | "record Pair: Copy + 'a + 'b { left: &'a u32, right: &'b u32 } fn f 'x 'y (left: &'x u32, right: &'y u32) -> Pair 'x 'y { return Pair 'x 'y { left, right }; }", |
| 620 | "record Pair: 'a + Copy + 'b { left: &'a u32, right: &'b u32 } fn f 'x 'y (left: &'x u32, right: &'y u32) -> Pair 'x 'y { return Pair 'x 'y { left, right }; }", |
| 621 | "record Pair: 'a + 'b + Copy { left: &'a u32, right: &'b u32 } fn f 'x 'y (left: &'x u32, right: &'y u32) -> Pair 'x 'y { return Pair 'x 'y { left, right }; }", |
| 622 | ] { |
| 623 | let mut arena = super::testArena(); |
| 624 | let storage: 'test = &mut arena in { |
| 625 | let mut res = super::testResolver(storage); |
| 626 | let result = try super::resolveProgramStr(&mut res, program); |
| 627 | try super::expectNoErrors(&result); |
| 628 | } |
| 629 | } |
| 630 | } |
| 631 | |
| 632 | /// Exact application keys include the base and the ordered region arguments. |
| 633 | @test unsafe fn testNominalApplicationKeys() throws (testing::TestError) { |
| 634 | let mut arena = super::testArena(); |
| 635 | let storage: 'test = &mut arena in { |
| 636 | let mut res = super::testResolver(storage); |
| 637 | let result = try super::resolveProgramStr(&mut res, |
| 638 | "record N: 'r + 's + Copy { value: u32 } record M: 'r + 's + Copy { value: u32 } fn f 'a 'b (x: N 'a 'b, y: N 'a 'b, z: N 'b 'a, w: N 'a 'a, v: M 'a 'b) {}" |
| 639 | ); |
| 640 | try super::expectNoErrors(&result); |
| 641 | let func = try super::getBlockStmt(result.root, 2); |
| 642 | let ty = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
| 643 | let case resolver::Type::Fn(info) = ty else throw testing::TestError::Failed; |
| 644 | for param, i in info.paramTypes { |
| 645 | for other, j in info.paramTypes { |
| 646 | assert (param == other) == (i == j or (i < 2 and j < 2)); |
| 647 | } |
| 648 | } |
| 649 | let mut count: u32 = 0; |
| 650 | let mut cursor = res.applications; |
| 651 | while let entry = cursor { |
| 652 | assert entry.arguments.len == 2; |
| 653 | assert resolver::nominalApplication(entry.view) == entry; |
| 654 | set count += 1; |
| 655 | set cursor = entry.next; |
| 656 | } |
| 657 | assert count == 4; |
| 658 | } |
| 659 | } |
| 660 | |
| 661 | /// Exact arguments share one descriptor and all applications share the source layout. |
| 662 | @test unsafe fn testNominalRegionApplications() throws (testing::TestError) { |
| 663 | let mut testArena19 = super::testArena(); |
| 664 | let testStorage19: 'test19 = &mut testArena19 in { |
| 665 | let mut res = super::testResolver(testStorage19); |
| 666 | let result = try super::resolveProgramStr(&mut res, |
| 667 | "record N: 'r + Copy { value: u32 } fn f 'a 'b (x: N 'a, y: N 'a, z: N 'b) {}" |
| 668 | ); |
| 669 | try super::expectNoErrors(&result); |
| 670 | let func = try super::getBlockStmt(result.root, 1); |
| 671 | let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
| 672 | let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed; |
| 673 | let case resolver::Type::Nominal(x) = *info.paramTypes[0] else throw testing::TestError::Failed; |
| 674 | let case resolver::Type::Nominal(y) = *info.paramTypes[1] else throw testing::TestError::Failed; |
| 675 | let case resolver::Type::Nominal(z) = *info.paramTypes[2] else throw testing::TestError::Failed; |
| 676 | try testing::expect(x == y); |
| 677 | try testing::expect(x <> z); |
| 678 | try testing::expect(not resolver::typesEqual(*info.paramTypes[0], *info.paramTypes[2])); |
| 679 | let a = resolver::nominalApplication(x) else throw testing::TestError::Failed; |
| 680 | let b = resolver::nominalApplication(z) else throw testing::TestError::Failed; |
| 681 | try testing::expect(a.base == b.base); |
| 682 | try testing::expect(a.arguments[0].id <> b.arguments[0].id); |
| 683 | let case resolver::NominalType::Record(base) = *a.base else throw testing::TestError::Failed; |
| 684 | let case resolver::NominalType::Record(first) = *x else throw testing::TestError::Failed; |
| 685 | let case resolver::NominalType::Record(second) = *z else throw testing::TestError::Failed; |
| 686 | try testing::expect(base.layout == first.layout); |
| 687 | try testing::expect(first.layout == second.layout); |
| 688 | try testing::expect(first.layout.size == 4); |
| 689 | } |
| 690 | } |
| 691 | |
| 692 | |
| 693 | /// Applications introduced in successive function bodies are fully resolved. |
| 694 | @test unsafe fn testNominalCompletionAcrossBodies() throws (testing::TestError) { |
| 695 | let mut arena = super::testArena(); |
| 696 | let storage: 'test = &mut arena in { |
| 697 | let mut res = super::testResolver(storage); |
| 698 | let result = try super::resolveProgramStr(&mut res, |
| 699 | "record N: 'r + Copy { value: &'r u32 } fn f 'a (x: &'a u32) { let n = N 'a { value: x }; } fn g 'b (x: &'b u32) { let n = N 'b { value: x }; }" |
| 700 | ); |
| 701 | try super::expectNoErrors(&result); |
| 702 | let mut count: u32 = 0; |
| 703 | let mut cursor = res.applications; |
| 704 | while let applied = cursor { |
| 705 | let case resolver::NominalType::Record(body) = *applied.view else throw testing::TestError::Failed; |
| 706 | assert body.application == applied; |
| 707 | assert body.fields.len == 1; |
| 708 | set count += 1; |
| 709 | set cursor = applied.next; |
| 710 | } |
| 711 | assert count == 2; |
| 712 | } |
| 713 | } |
| 714 | |
| 715 | /// Recursive fields refer to their own exact applied descriptor. |
| 716 | @test unsafe fn testRecursiveNominalApplications() throws (testing::TestError) { |
| 717 | let mut testArena20 = super::testArena(); |
| 718 | let testStorage20: 'test20 = &mut testArena20 in { |
| 719 | let mut res = super::testResolver(testStorage20); |
| 720 | let result = try super::resolveProgramStr(&mut res, |
| 721 | "record N: 'r + Copy { next: ?*N 'r } fn f 'a 'b (x: N 'a, y: N 'b) {}" |
| 722 | ); |
| 723 | try super::expectNoErrors(&result); |
| 724 | let func = try super::getBlockStmt(result.root, 1); |
| 725 | let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
| 726 | let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed; |
| 727 | for parameter in info.paramTypes { |
| 728 | let case resolver::Type::Nominal(descriptor) = *parameter else throw testing::TestError::Failed; |
| 729 | let case resolver::NominalType::Record(body) = *descriptor else throw testing::TestError::Failed; |
| 730 | let case resolver::Type::Optional(inner) = body.fields[0].fieldType else throw testing::TestError::Failed; |
| 731 | let case resolver::Type::Pointer { target, .. } = *inner else throw testing::TestError::Failed; |
| 732 | try testing::expect(resolver::typesEqual(*target, *parameter)); |
| 733 | try testing::expect(body.layout.size == 8); |
| 734 | } |
| 735 | } |
| 736 | } |
| 737 | |
| 738 | /// An inline union payload retains its enclosing application's region arguments. |
| 739 | @test unsafe fn testNominalPayloadRegionCapture() throws (testing::TestError) { |
| 740 | let mut testArena21 = super::testArena(); |
| 741 | let testStorage21: 'test21 = &mut testArena21 in { |
| 742 | let mut res = super::testResolver(testStorage21); |
| 743 | let result = try super::resolveProgramStr(&mut res, |
| 744 | "record N: 'r + Copy { value: u32 } union U: 'r + Copy { Link { node: *N 'r }, Empty } fn f 'a (u: U 'a, n: N 'a) {}" |
| 745 | ); |
| 746 | try super::expectNoErrors(&result); |
| 747 | let func = try super::getBlockStmt(result.root, 2); |
| 748 | let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
| 749 | let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed; |
| 750 | let case resolver::Type::Nominal(resolver::NominalType::Union(body)) = *info.paramTypes[0] |
| 751 | else throw testing::TestError::Failed; |
| 752 | let case resolver::Type::Nominal(resolver::NominalType::Record(payload)) = body.variants[0].valueType |
| 753 | else throw testing::TestError::Failed; |
| 754 | let case resolver::Type::Pointer { target, .. } = payload.fields[0].fieldType |
| 755 | else throw testing::TestError::Failed; |
| 756 | try testing::expect(resolver::typesEqual(*target, *info.paramTypes[1])); |
| 757 | let applied = body.application else throw testing::TestError::Failed; |
| 758 | let case resolver::NominalType::Union(base) = *applied.base else throw testing::TestError::Failed; |
| 759 | try testing::expect(body.layout == base.layout); |
| 760 | } |
| 761 | } |
| 762 | |
| 763 | /// Nominal applications require exactly the declared number of region arguments. |
| 764 | @test unsafe fn testNominalRegionArgumentCount() throws (testing::TestError) { |
| 765 | for program in [ |
| 766 | "record N: 'r { value: u32 } fn f(n: N) {}", |
| 767 | "record N { value: u32 } fn f 'r (n: N 'r) {}", |
| 768 | "record N: 'r + 's { value: u32 } fn f 'a (n: N 'a) {}", |
| 769 | "record N: 'r { value: u32 } fn f 'a 'b (n: N 'a 'b) {}", |
| 770 | "record N: 'r { value: u32 } fn f() { let n = N { value: 1 }; }", |
| 771 | "union U: 'r { Empty } fn f() { let u = U::Empty; }", |
| 772 | ] { |
| 773 | let mut testArena22 = super::testArena(); |
| 774 | let testStorage22: 'test22 = &mut testArena22 in { |
| 775 | let mut res = super::testResolver(testStorage22); |
| 776 | let result = try super::resolveProgramStr(&mut res, program); |
| 777 | let error = try super::expectError(&result); |
| 778 | let case resolver::ErrorKind::RegionArgumentCount(_) = error.kind else throw testing::TestError::Failed; |
| 779 | } |
| 780 | } |
| 781 | } |
| 782 | |
| 783 | /// Type arguments are invariant in values, constructor hints, and patterns. |
| 784 | @test unsafe fn testNominalRegionInvariance() throws (testing::TestError) { |
| 785 | for program in [ |
| 786 | "record N: 'r + Copy { value: u32 } fn f 'a 'b (n: N 'a) { let x: N 'b = n; }", |
| 787 | "record N: 'r + Copy { value: u32 } unsafe fn f 'a 'b (n: N 'a) { let x: N 'b = n; }", |
| 788 | "record N: 'r + Copy { value: u32 } fn f 'a 'b () { let n: N 'a = N 'b { value: 1 }; }", |
| 789 | "record N: 'r + Copy { value: u32 } fn f 'a 'b (n: N 'a) { let case N 'b { value } = n else panic; }", |
| 790 | "record N: 'r + Copy (u32); fn f 'a 'b (n: N 'a) { let case N 'b (value) = n else panic; }", |
| 791 | "record N: 'r + Copy { value: u32 } record M: 'r + Copy { value: u32 } fn f 'a (n: N 'a) { let case M 'a { value } = n else panic; }", |
| 792 | ] { |
| 793 | let mut testArena23 = super::testArena(); |
| 794 | let testStorage23: 'test23 = &mut testArena23 in { |
| 795 | let mut res = super::testResolver(testStorage23); |
| 796 | let result = try super::resolveProgramStr(&mut res, program); |
| 797 | let error = try super::expectError(&result); |
| 798 | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
| 799 | } |
| 800 | } |
| 801 | } |
| 802 | |
| 803 | /// Region inference must agree between an outer reference and its embedded nominal type. |
| 804 | @test unsafe fn testNominalRegionInferenceConflict() throws (testing::TestError) { |
| 805 | for program in [ |
| 806 | "record N: 'r + Copy { value: u32 } fn take 'r (n: &'r N 'r) {} fn f 'a 'b (n: &'a N 'b) { take(n); }", |
| 807 | "record N: 'r + Copy { value: u32 } fn take 'r (n: N 'r, m: N 'r) {} fn f 'a 'b (n: N 'a, m: N 'b) { take(n, m); }", |
| 808 | ] { |
| 809 | let mut testArena24 = super::testArena(); |
| 810 | let testStorage24: 'test24 = &mut testArena24 in { |
| 811 | let mut res = super::testResolver(testStorage24); |
| 812 | let result = try super::resolveProgramStr(&mut res, program); |
| 813 | let error = try super::expectError(&result); |
| 814 | let case resolver::ErrorKind::RegionInference(_) = error.kind else throw testing::TestError::Failed; |
| 815 | } |
| 816 | } |
| 817 | } |
| 818 | |
| 819 | /// Applications must satisfy the source declaration's explicit parent relations. |
| 820 | @test unsafe fn testNominalRegionParentConstraint() throws (testing::TestError) { |
| 821 | let mut testArena25 = super::testArena(); |
| 822 | let testStorage25: 'test25 = &mut testArena25 in { |
| 823 | let mut res = super::testResolver(testStorage25); |
| 824 | let result = try super::resolveProgramStr(&mut res, |
| 825 | "record N: 'r + 's where 'r: 's { value: u32 } fn f 'a 'b (n: N 'a 'b) {}" |
| 826 | ); |
| 827 | let error = try super::expectError(&result); |
| 828 | let case resolver::ErrorKind::RegionParent(_) = error.kind else throw testing::TestError::Failed; |
| 829 | } |
| 830 | } |
| 831 | |
| 832 | /// An applied affine type retains the source declaration's move rule. |
| 833 | @test unsafe fn testNominalRegionMoveRule() throws (testing::TestError) { |
| 834 | let mut testArena26 = super::testArena(); |
| 835 | let testStorage26: 'test26 = &mut testArena26 in { |
| 836 | let mut res = super::testResolver(testStorage26); |
| 837 | let result = try super::resolveProgramStr(&mut res, |
| 838 | "record N: 'r { value: u32 } fn f 'a (n: N 'a) { let first = n; let second = n; }" |
| 839 | ); |
| 840 | let error = try super::expectError(&result); |
| 841 | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
| 842 | } |
| 843 | } |
| 844 | |
| 845 | /// By-value cycles have no layout, including cycles through exact applications. |
| 846 | @test unsafe fn testNominalRegionValueCycle() throws (testing::TestError) { |
| 847 | for program in [ |
| 848 | "record N: 'r { next: N 'r }", |
| 849 | "record N: 'r { next: ?N 'r }", |
| 850 | "union U: 'r { More(U 'r), Empty }", |
| 851 | "record N: 'r { m: M 'r } record M: 'r { n: N 'r }", |
| 852 | ] { |
| 853 | let mut testArena27 = super::testArena(); |
| 854 | let testStorage27: 'test27 = &mut testArena27 in { |
| 855 | let mut res = super::testResolver(testStorage27); |
| 856 | let result = try super::resolveProgramStr(&mut res, program); |
| 857 | let error = try super::expectError(&result); |
| 858 | let case resolver::ErrorKind::RecursiveType = error.kind else throw testing::TestError::Failed; |
| 859 | } |
| 860 | } |
| 861 | } |
| 862 | |
| 863 | /// Recursive substitutions can permute arguments without expanding the type graph. |
| 864 | @test unsafe fn testNominalRegionPermutation() throws (testing::TestError) { |
| 865 | let mut testArena28 = super::testArena(); |
| 866 | let testStorage28: 'test28 = &mut testArena28 in { |
| 867 | let mut res = super::testResolver(testStorage28); |
| 868 | let result = try super::resolveProgramStr(&mut res, |
| 869 | "record N: 'r + 's + Copy { next: ?*N 's 'r } fn f 'a 'b (x: N 'a 'b, y: N 'b 'a) {}" |
| 870 | ); |
| 871 | try super::expectNoErrors(&result); |
| 872 | let func = try super::getBlockStmt(result.root, 1); |
| 873 | let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
| 874 | let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed; |
| 875 | for parameter, i in info.paramTypes { |
| 876 | let case resolver::Type::Nominal(resolver::NominalType::Record(body)) = *parameter |
| 877 | else throw testing::TestError::Failed; |
| 878 | let case resolver::Type::Optional(inner) = body.fields[0].fieldType else throw testing::TestError::Failed; |
| 879 | let case resolver::Type::Pointer { target, .. } = *inner else throw testing::TestError::Failed; |
| 880 | try testing::expect(resolver::typesEqual(*target, *info.paramTypes[1 - i])); |
| 881 | } |
| 882 | } |
| 883 | } |
| 884 | |
| 885 | /// Calls transfer named exclusive references on normal and error paths. |
| 886 | @test unsafe fn testRegionalCallOwnershipTransfer() throws (testing::TestError) { |
| 887 | for program in [ |
| 888 | "fn take 'r (p: &'r mut u32) {} fn run 'r (p: &'r mut u32) { take(p); take(p); }", |
| 889 | "fn take 'r (p: &'r mut u32) {} unsafe fn run 'r (p: &'r mut u32) { take(p); set *p = 1; }", |
| 890 | "fn take 'r (p: &'r mut [u32]) {} fn run 'r (p: &'r mut [u32]) { take(p); p[0]; }", |
| 891 | "fn take 'r (p: &'r mut u32) {} fn run 'r (p: &'r mut u32) { let f = take 'r; f(p); set *p = 1; }", |
| 892 | "union E { Bad } fn take 'r (p: &'r mut u32) throws (E) { throw E::Bad; } fn run 'r (p: &'r mut u32) { try take(p) catch {}; set *p = 1; }", |
| 893 | "fn take 'r (p: &'r mut u32) {} fn run 'r (p: &'r mut u32, c: bool) { if c { take(p); } set *p = 1; }", |
| 894 | ] { |
| 895 | let mut testArena29 = super::testArena(); |
| 896 | let testStorage29: 'test29 = &mut testArena29 in { |
| 897 | let mut res = super::testResolver(testStorage29); |
| 898 | let result = try super::resolveProgramStr(&mut res, program); |
| 899 | let error = try super::expectError(&result); |
| 900 | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
| 901 | } |
| 902 | } |
| 903 | } |
| 904 | |
| 905 | /// Full-region projections protect their source after a local binding leaves scope. |
| 906 | @test unsafe fn testRegionalProjectionLifetime() throws (testing::TestError) { |
| 907 | for program in [ |
| 908 | "fn f 'r (p: &'r mut u32) { let shared = &*p; let copy = shared; set *p = 1; }", |
| 909 | "unsafe fn f 'r (p: &'r mut u32) { let shared = &*p; let copy = shared; set *p = 1; }", |
| 910 | "fn f 'r (p: &'r mut u32, q: &'r u32) { let mut cursor = q; set cursor = &*p; set *p = 1; }", |
| 911 | "unsafe fn f 'r (p: &'r mut u32, q: &'r u32) { let mut cursor = q; set cursor = &*p; set *p = 1; }", |
| 912 | "fn f 'r (p: &'r mut u32) { { let q = &mut *p; set *q = 1; } set *p = 2; }", |
| 913 | "fn f 'r (p: &'r mut u32) { { let q = &*p; } set *p = 2; }", |
| 914 | "unsafe fn f 'r (p: &'r mut u32) { { let q = &*p; } set *p = 2; }", |
| 915 | "fn f 'r (p: &'r mut u32) { { let q = &mut *p; let moved = q; } *p; }", |
| 916 | "record P { a: u32, b: u32 } fn f 'r (p: &'r mut P) { { let q = &mut p.a; } p.a; }", |
| 917 | "record P { a: u32, b: u32 } fn f 'r (p: &'r mut P) { { let q = &p.a; } set p.a = 2; }", |
| 918 | ] { |
| 919 | let mut testArena30 = super::testArena(); |
| 920 | let testStorage30: 'test30 = &mut testArena30 in { |
| 921 | let mut res = super::testResolver(testStorage30); |
| 922 | let result = try super::resolveProgramStr(&mut res, program); |
| 923 | let error = try super::expectError(&result); |
| 924 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 925 | } |
| 926 | } |
| 927 | } |
| 928 | |
| 929 | /// A regional loan on any live branch remains active at the branch join. |
| 930 | @test unsafe fn testRegionalProjectionBranchJoin() throws (testing::TestError) { |
| 931 | for program in [ |
| 932 | "fn f 'r (p: &'r mut u32, c: bool) { if c { let q = &mut *p; } set *p = 2; }", |
| 933 | "fn f 'r (p: &'r mut u32, c: bool) { if c {} else { let q = &*p; } set *p = 2; }", |
| 934 | "fn f 'r (p: &'r mut u32, c: bool) { match c { case true => { let q = &mut *p; } else => {} } *p; }", |
| 935 | "fn f 'r (p: &'r mut u32, c: ?u32) { if let n = c { let q = &*p; } set *p = 2; }", |
| 936 | ] { |
| 937 | let mut testArena31 = super::testArena(); |
| 938 | let testStorage31: 'test31 = &mut testArena31 in { |
| 939 | let mut res = super::testResolver(testStorage31); |
| 940 | let result = try super::resolveProgramStr(&mut res, program); |
| 941 | let error = try super::expectError(&result); |
| 942 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 943 | } |
| 944 | } |
| 945 | } |
| 946 | |
| 947 | /// Loop back edges carry regional loans into later iterations. |
| 948 | @test unsafe fn testRegionalProjectionLoopBackEdge() throws (testing::TestError) { |
| 949 | for program in [ |
| 950 | "fn f 'r (p: &'r mut u32) { for i in 0..2 { let q = &mut *p; } }", |
| 951 | "fn f 'r (p: &'r mut u32) { for i in 0..2 { set *p = 1; let q = &*p; } }", |
| 952 | "fn f 'r (p: &'r mut u32) { for i in 0..2 { set *p = 1; let q = &*p; continue; } }", |
| 953 | "fn f 'r (p: &'r mut u32, c: bool) { while c { let q = &mut *p; } }", |
| 954 | "fn f 'r (p: &'r mut u32, c: ?u32) { while let n = c { let q = &mut *p; } }", |
| 955 | "fn f 'r (p: &'r mut u32) { loop { let q = &mut *p; } }", |
| 956 | "fn f 'r (p: &'r mut u32, c: bool) { loop { if c { set *p = 1; break; } let q = &*p; } }", |
| 957 | ] { |
| 958 | let mut testArena32 = super::testArena(); |
| 959 | let testStorage32: 'test32 = &mut testArena32 in { |
| 960 | let mut res = super::testResolver(testStorage32); |
| 961 | let result = try super::resolveProgramStr(&mut res, program); |
| 962 | let error = try super::expectError(&result); |
| 963 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 964 | } |
| 965 | } |
| 966 | } |
| 967 | |
| 968 | /// Loans in caller-supplied regions survive loop and nested-region exits. |
| 969 | @test unsafe fn testRegionalProjectionLoopExit() throws (testing::TestError) { |
| 970 | for program in [ |
| 971 | "fn f 'r (p: &'r mut u32) { loop { let q = &mut *p; break; } set *p = 2; }", |
| 972 | "fn f 'r (p: &'r mut u32, c: bool) { while c { let q = &*p; break; } else {} set *p = 2; }", |
| 973 | "fn f 'r (p: &'r mut u32) { for i in 0..2 { let q = &*p; break; } else {} set *p = 2; }", |
| 974 | "fn f 'r (p: &'r mut u32) { let x: u32 = 0; let v: 'child = &x where 'r: 'child in { let q = &*p; } set *p = 2; }", |
| 975 | ] { |
| 976 | let mut testArena33 = super::testArena(); |
| 977 | let testStorage33: 'test33 = &mut testArena33 in { |
| 978 | let mut res = super::testResolver(testStorage33); |
| 979 | let result = try super::resolveProgramStr(&mut res, program); |
| 980 | let error = try super::expectError(&result); |
| 981 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 982 | } |
| 983 | } |
| 984 | } |
| 985 | |
| 986 | /// Region erasure cannot merge distinct error alternatives in one signature. |
| 987 | @test unsafe fn testRegionalErrorAmbiguity() throws (testing::TestError) { |
| 988 | for program in [ |
| 989 | "record E: 'r + Copy {} fn f 'a 'b () throws (E 'a, E 'b) {}", |
| 990 | "record E: 'r + Copy {} unsafe fn f 'a 'b () throws (E 'a, E 'b) {}", |
| 991 | "record E: 'r + Copy {} record Callback: 'a + 'b { f: fn() throws (E 'a, E 'b) }", |
| 992 | "fn f 'a 'b () throws (&'a u32, &'b u32) {}", |
| 993 | ] { |
| 994 | let mut testArena34 = super::testArena(); |
| 995 | let testStorage34: 'test34 = &mut testArena34 in { |
| 996 | let mut res = super::testResolver(testStorage34); |
| 997 | let result = try super::resolveProgramStr(&mut res, program); |
| 998 | try super::expectErrorKind(&result, resolver::ErrorKind::AmbiguousRegionalError); |
| 999 | } |
| 1000 | } |
| 1001 | } |
| 1002 | |
| 1003 | /// Moving an exclusive reference into an aggregate transfers its ownership. |
| 1004 | @test unsafe fn testRegionalAggregateMove() throws (testing::TestError) { |
| 1005 | for program in [ |
| 1006 | "record W: 'r { p: &'r mut u32 } fn f 'r (p: &'r mut u32) { let w = W 'r { p }; set *p = 1; }", |
| 1007 | "record W: 'r { p: &'r mut u32 } unsafe fn f 'r (p: &'r mut u32) { let w = W 'r { p }; set *p = 1; }", |
| 1008 | "record W: 'r { p: &'r mut u32 } fn f 'r (w: W 'r) { let case W { p } = w else panic; let again = w; }", |
| 1009 | "union W: 'r { P { p: &'r mut u32 } } fn f 'r (p: &'r mut u32) { let w = W 'r::P { p }; set *p = 1; }", |
| 1010 | "fn f 'r (p: &'r mut u32) { let a: [&'r mut u32; 1] = [p]; set *p = 1; }", |
| 1011 | "fn f 'r (p: &'r mut u32) { let a: ?&'r mut u32 = p; set *p = 1; }", |
| 1012 | ] { |
| 1013 | let mut testArena35 = super::testArena(); |
| 1014 | let testStorage35: 'test35 = &mut testArena35 in { |
| 1015 | let mut res = super::testResolver(testStorage35); |
| 1016 | let result = try super::resolveProgramStr(&mut res, program); |
| 1017 | let error = try super::expectError(&result); |
| 1018 | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
| 1019 | } |
| 1020 | } |
| 1021 | } |
| 1022 | |
| 1023 | /// Aggregate transfers do not end a projection loan on its source. |
| 1024 | @test unsafe fn testRegionalAggregateLoan() throws (testing::TestError) { |
| 1025 | for program in [ |
| 1026 | "record W: 'r { p: &'r mut u32 } fn f 'r (p: &'r mut u32) { { let w = W 'r { p: &mut *p }; } set *p = 1; }", |
| 1027 | "record R: 'r + Copy { p: &'r u32 } fn f 'r (p: &'r mut u32) { { let r = R 'r { p: &*p }; } set *p = 1; }", |
| 1028 | "record R: 'r + Copy { p: &'r u32 } fn take 'r (r: R 'r) {} fn f 'r (p: &'r mut u32) { take(R 'r { p: &*p }); set *p = 1; }", |
| 1029 | "fn id 'r (p: &'r mut u32) -> &'r mut u32 { return p; } fn f 'r (p: &'r mut u32) { let q = id(p); { let other = &mut *q; } set *q = 1; }", |
| 1030 | ] { |
| 1031 | let mut testArena36 = super::testArena(); |
| 1032 | let testStorage36: 'test36 = &mut testArena36 in { |
| 1033 | let mut res = super::testResolver(testStorage36); |
| 1034 | let result = try super::resolveProgramStr(&mut res, program); |
| 1035 | let error = try super::expectError(&result); |
| 1036 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 1037 | } |
| 1038 | } |
| 1039 | } |
| 1040 | |
| 1041 | /// Local storage cannot acquire a caller region through returns or aggregate hints. |
| 1042 | @test unsafe fn testRegionalStorageEscape() throws (testing::TestError) { |
| 1043 | for program in [ |
| 1044 | "fn f 'r () -> &'r u32 { let x: u32 = 1; return &x; }", |
| 1045 | "unsafe fn f 'r () -> &'r u32 { let x: u32 = 1; return &x; }", |
| 1046 | "record R: 'r + Copy { p: &'r u32 } fn f 'r () -> R 'r { let x: u32 = 1; return R 'r { p: &x }; }", |
| 1047 | "record R: 'r + Copy { p: &'r u32 } fn f 'r () -> R 'r { let x: u32 = 1; let p: 'short = &x in { return R 'short { p }; } }", |
| 1048 | "record R: 'r + Copy { p: &'r u32 } fn f 'r (p: &'r u32) -> &'r R 'r { let r = R 'r { p }; return &r; }", |
| 1049 | "record R: 'r + Copy { p: &'r u32 } fn f 'r (p: &'r u32) { let mut outer = R 'r { p }; let x: u32 = 1; let q: 'short = &x in { set outer = R 'short { p: q }; } }", |
| 1050 | ] { |
| 1051 | let mut testArena37 = super::testArena(); |
| 1052 | let testStorage37: 'test37 = &mut testArena37 in { |
| 1053 | let mut res = super::testResolver(testStorage37); |
| 1054 | let result = try super::resolveProgramStr(&mut res, program); |
| 1055 | let error = try super::expectError(&result); |
| 1056 | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
| 1057 | } |
| 1058 | } |
| 1059 | } |
| 1060 | |
| 1061 | /// Reference fields retain their ownership and storage restrictions. |
| 1062 | @test unsafe fn testRegionalFieldRestrictions() throws (testing::TestError) { |
| 1063 | { |
| 1064 | let mut testArena38 = super::testArena(); |
| 1065 | let testStorage38: 'test38 = &mut testArena38 in { |
| 1066 | let mut res = super::testResolver(testStorage38); |
| 1067 | let result = try super::resolveProgramStr(&mut res, "record W: 'r { p: &'r mut u32 } fn f 'r (w: W 'r) { let p = w.p; }"); |
| 1068 | try super::expectErrorKind(&result, resolver::ErrorKind::LinearPartialMove); |
| 1069 | } |
| 1070 | } |
| 1071 | { |
| 1072 | let mut testArena39 = super::testArena(); |
| 1073 | let testStorage39: 'test39 = &mut testArena39 in { |
| 1074 | let mut res = super::testResolver(testStorage39); |
| 1075 | let result = try super::resolveProgramStr(&mut res, "record R: 'r { p: &u32 }"); |
| 1076 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidRefPosition); |
| 1077 | } |
| 1078 | } |
| 1079 | { |
| 1080 | let mut testArena40 = super::testArena(); |
| 1081 | let testStorage40: 'test40 = &mut testArena40 in { |
| 1082 | let mut res = super::testResolver(testStorage40); |
| 1083 | let result = try super::resolveProgramStr(&mut res, "fn f 'r (p: &'r u32) { static r: &'r u32 = undefined; }"); |
| 1084 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidRefPosition); |
| 1085 | } |
| 1086 | } |
| 1087 | } |
| 1088 | |
| 1089 | /// Call tracking includes all explicit arguments and the implicit receiver. |
| 1090 | @test unsafe fn testCallLoanScratchCapacity() throws (testing::TestError) { |
| 1091 | let mut arena = super::testArena(); |
| 1092 | let storage: 'test = &mut arena in { |
| 1093 | let mut res = super::testResolver(storage); |
| 1094 | let result = try super::resolveProgramStr(&mut res, |
| 1095 | "record R: Copy { value: u32 } fn (r: &mut R) take(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: &mut u32) {} fn run() { let mut r = R { value: 0 }; let mut other: u32 = 0; r.take(0, 0, 0, 0, 0, 0, 0, &mut other); set r.value = 1; set other = 2; }" |
| 1096 | ); |
| 1097 | try super::expectNoErrors(&result); |
| 1098 | } |
| 1099 | for program in [ |
| 1100 | "record R: Copy { value: u32 } fn (r: &mut R) take(a: u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: &mut u32) {} fn run() { let mut r = R { value: 0 }; r.take(0, 0, 0, 0, 0, 0, 0, &mut r.value); }", |
| 1101 | "fn take(a: &mut u32, b: u32, c: u32, d: u32, e: u32, f: u32, g: u32, h: &mut u32) {} fn run() { let mut value: u32 = 0; take(&mut value, 0, 0, 0, 0, 0, 0, &mut value); }", |
| 1102 | ] { |
| 1103 | let mut errorArena = super::testArena(); |
| 1104 | let errorStorage: 'error = &mut errorArena in { |
| 1105 | let mut res = super::testResolver(errorStorage); |
| 1106 | let result = try super::resolveProgramStr(&mut res, program); |
| 1107 | let error = try super::expectError(&result); |
| 1108 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 1109 | } |
| 1110 | } |
| 1111 | } |
| 1112 | |
| 1113 | /// Loan compaction preserves outer-region conflicts after child slots expire. |
| 1114 | @test unsafe fn testRegionalLoanCompactionConflict() throws (testing::TestError) { |
| 1115 | for program in [ |
| 1116 | "fn f 'r (p: &'r mut u32) { let value: u32 = 0; let child: 'child = &value where 'r: 'child in { let local = &*child; let retained = &*p; } set *p = 1; }", |
| 1117 | "fn f 'r (p: &'r mut u32) { let value: u32 = 0; loop { let child: 'child = &value where 'r: 'child in { let local = &*child; let retained = &*p; } break; } set *p = 1; }", |
| 1118 | ] { |
| 1119 | let mut arena = super::testArena(); |
| 1120 | let storage: 'test = &mut arena in { |
| 1121 | let mut res = super::testResolver(storage); |
| 1122 | let result = try super::resolveProgramStr(&mut res, program); |
| 1123 | let error = try super::expectError(&result); |
| 1124 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 1125 | } |
| 1126 | } |
| 1127 | } |
| 1128 | |
| 1129 | /// Regional projection tracking limits simultaneous live projections. |
| 1130 | @test unsafe fn testRegionalLoanCapacity() throws (testing::TestError) { |
| 1131 | let mut reuseArena = super::testArena(); |
| 1132 | let reuseStorage: 'reuse = &mut reuseArena in { |
| 1133 | let mut res = super::testResolver(reuseStorage); |
| 1134 | let result = try super::resolveProgramStr(&mut res, "fn f 'r (p: &'r u32) { let outer: 'outer = &*p where 'r: 'outer in { let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } let q: 'x = &*outer in { &*q; } } }"); |
| 1135 | try super::expectNoErrors(&result); |
| 1136 | } |
| 1137 | let mut testArena41 = super::testArena(); |
| 1138 | let testStorage41: 'test41 = &mut testArena41 in { |
| 1139 | let mut res = super::testResolver(testStorage41); |
| 1140 | let result = try super::resolveProgramStr(&mut res, "fn f 'r (p: &'r u32) { [&*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p, &*p]; }"); |
| 1141 | try super::expectErrorKind(&result, resolver::ErrorKind::RegionalLoanOverflow); |
| 1142 | } |
| 1143 | } |
| 1144 | |
| 1145 | /// Shared regional parameters consume exclusive arguments that can be retained. |
| 1146 | @test unsafe fn testRegionalSharedCallTransfer() throws (testing::TestError) { |
| 1147 | for program in [ |
| 1148 | "fn read 'r (p: &'r u32) -> &'r u32 { return p; } fn f 'r (p: &'r mut u32) -> u32 { let q = read(p); set *p = 2; return *q; }", |
| 1149 | "fn read 'r (p: &'r u32) -> &'r u32 { return p; } unsafe fn f 'r (p: &'r mut u32) { let q = read(p); set *p = 2; }", |
| 1150 | "fn read 'r (p: &'r [u32]) -> &'r [u32] { return p; } fn f 'r (p: &'r mut [u32]) { let q = read(p); set p[0] = 2; }", |
| 1151 | "fn read 'r (p: &'r u32) -> &'r u32 { return p; } fn f 'r (p: &'r mut u32) { let callback = read 'r; let q = callback(p); set *p = 2; }", |
| 1152 | ] { |
| 1153 | let mut testArena42 = super::testArena(); |
| 1154 | let testStorage42: 'test42 = &mut testArena42 in { |
| 1155 | let mut res = super::testResolver(testStorage42); |
| 1156 | let result = try super::resolveProgramStr(&mut res, program); |
| 1157 | let error = try super::expectError(&result); |
| 1158 | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
| 1159 | } |
| 1160 | } |
| 1161 | } |
| 1162 | |
| 1163 | /// Values stored in a regional destination must cover its full lifetime. |
| 1164 | @test unsafe fn testRegionalStoreLifetime() throws (testing::TestError) { |
| 1165 | for program in [ |
| 1166 | "fn f 'a 'b (dst: &'a mut ?&'b u32, p: &'b u32) { set *dst = p; }", |
| 1167 | "unsafe fn f 'a 'b (dst: &'a mut ?&'b u32, p: &'b u32) { set *dst = p; }", |
| 1168 | "fn f 'a 'b (dst: &'a mut ?&'b u32, p: &'b u32) where 'a: 'b { set *dst = p; }", |
| 1169 | "record R: 'r + Copy { p: &'r u32 } fn f 'a 'b (dst: &'a mut R 'b, p: &'b u32) { set *dst = R 'b { p }; }", |
| 1170 | "fn f 'a 'b (dst: &'a mut [&'b u32], p: &'b u32) { set dst[0] = p; }", |
| 1171 | "fn f 'a 'b (dst: &'a mut [&'b u32], p: &'b u32) { set dst[..] = p; }", |
| 1172 | "fn f 'a 'b (dst: &'a mut fn(&'b u32), callback: fn(&'b u32)) { set *dst = callback; }", |
| 1173 | ] { |
| 1174 | let mut testArena43 = super::testArena(); |
| 1175 | let testStorage43: 'test43 = &mut testArena43 in { |
| 1176 | let mut res = super::testResolver(testStorage43); |
| 1177 | let result = try super::resolveProgramStr(&mut res, program); |
| 1178 | let error = try super::expectError(&result); |
| 1179 | let case resolver::ErrorKind::RegionEscape(_) = error.kind else throw testing::TestError::Failed; |
| 1180 | } |
| 1181 | } |
| 1182 | } |
| 1183 | |
| 1184 | /// A session holds the arena loan after the interface is moved or discarded. |
| 1185 | @test unsafe fn testSessionArenaLoan() throws (testing::TestError) { |
| 1186 | for program in [ |
| 1187 | "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { alloc::reset(arena); } }", |
| 1188 | "use std::lang::alloc; unsafe fn f(arena: &mut alloc::Arena) { use *arena as s in { alloc::reset(arena); } }", |
| 1189 | "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { let moved = s; alloc::reset(arena); } }", |
| 1190 | "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { let _ = s; set arena.offset = 0; } }", |
| 1191 | "use std::lang::alloc; fn f(arena: &mut alloc::Arena) { use *arena as s in { use *arena as t in {} } }", |
| 1192 | ] { |
| 1193 | let mut testArena44 = super::testArena(); |
| 1194 | let testStorage44: 'test44 = &mut testArena44 in { |
| 1195 | let mut res = super::testResolver(testStorage44); |
| 1196 | let result = try super::resolveSessionProgramStr(&mut res, program); |
| 1197 | let error = try super::expectError(&result); |
| 1198 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 1199 | } |
| 1200 | } |
| 1201 | } |
| 1202 | |
| 1203 | /// Session interfaces have affine ownership and invariant region arguments. |
| 1204 | @test unsafe fn testSessionOwnership() throws (testing::TestError) { |
| 1205 | for program in [ |
| 1206 | "fn f 'r (s: Session 'r) { let moved = s; let again = s; }", |
| 1207 | "unsafe fn f 'r (s: Session 'r) { let moved = s; let again = s; }", |
| 1208 | "fn take 'r (s: Session 'r) {} fn f 'r (s: Session 'r) { take(s); take(s); }", |
| 1209 | ] { |
| 1210 | let mut testArena45 = super::testArena(); |
| 1211 | let testStorage45: 'test45 = &mut testArena45 in { |
| 1212 | let mut res = super::testResolver(testStorage45); |
| 1213 | let result = try super::resolveProgramStr(&mut res, program); |
| 1214 | let error = try super::expectError(&result); |
| 1215 | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
| 1216 | } |
| 1217 | } |
| 1218 | { |
| 1219 | let mut testArena46 = super::testArena(); |
| 1220 | let testStorage46: 'test46 = &mut testArena46 in { |
| 1221 | let mut res = super::testResolver(testStorage46); |
| 1222 | let result = try super::resolveProgramStr(&mut res, |
| 1223 | "fn f 'r 's (s: Session 's) -> Session 'r where 'r: 's { return s; }"); |
| 1224 | let error = try super::expectError(&result); |
| 1225 | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
| 1226 | } |
| 1227 | } |
| 1228 | } |
| 1229 | |
| 1230 | /// Regional `use` blocks require a mutable allocation trait implementer. |
| 1231 | @test unsafe fn testSessionSource() throws (testing::TestError) { |
| 1232 | for program in [ |
| 1233 | "record Arena { data: *mut [u8], offset: u32 } fn f(arena: &mut Arena) { use *arena as s in {} }", |
| 1234 | "fn f(value: &mut u32) { use *value as s in {} }", |
| 1235 | ] { |
| 1236 | let mut testArena47 = super::testArena(); |
| 1237 | let testStorage47: 'test47 = &mut testArena47 in { |
| 1238 | let mut res = super::testResolver(testStorage47); |
| 1239 | let result = try super::resolveSessionProgramStr(&mut res, program); |
| 1240 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidSessionSource); |
| 1241 | } |
| 1242 | } |
| 1243 | let mut customArena = super::testArena(); |
| 1244 | let customStorage: 'custom = &mut customArena in { |
| 1245 | let mut res = super::testResolver(customStorage); |
| 1246 | let result = try super::resolveSessionProgramStr(&mut res, |
| 1247 | "use std::lang::alloc; record Wrapper: 'r { arena: &'r mut alloc::Arena } instance alloc::Alloc for Wrapper 'r { unsafe fn (wrapper: &mut Wrapper 'r) reserve(size: u32, alignment: u32) -> *unsafe mut opaque throws (alloc::AllocError) { panic; } unsafe fn (wrapper: &mut Wrapper 'r) reserveSlice(size: u32, alignment: u32, count: u32) -> *unsafe mut [opaque] throws (alloc::AllocError) { panic; } } fn f 'r (wrapper: &'r mut Wrapper 'r) { use *wrapper as session in { try! session.new(1 as u32); } }"); |
| 1248 | try super::expectNoErrors(&result); |
| 1249 | } |
| 1250 | } |
| 1251 | |
| 1252 | /// Region-dependent values cannot be manufactured from uninitialized storage. |
| 1253 | @test unsafe fn testRegionalUndefined() throws (testing::TestError) { |
| 1254 | for program in [ |
| 1255 | "unsafe fn f 'r () -> Session 'r { return undefined; }", |
| 1256 | "record R: 'r { s: Session 'r } unsafe fn f 'r () -> R 'r { return undefined; }", |
| 1257 | "unsafe fn f 'r () -> *unsafe Session 'r { return undefined; }", |
| 1258 | "unsafe fn f 'r () -> &'r u32 { return undefined; }", |
| 1259 | ] { |
| 1260 | let mut testArena48 = super::testArena(); |
| 1261 | let testStorage48: 'test48 = &mut testArena48 in { |
| 1262 | let mut res = super::testResolver(testStorage48); |
| 1263 | let result = try super::resolveProgramStr(&mut res, program); |
| 1264 | let error = try super::expectError(&result); |
| 1265 | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
| 1266 | } |
| 1267 | } |
| 1268 | } |
| 1269 | |
| 1270 | /// Pointer casts cannot introduce session or aggregate region dependencies. |
| 1271 | @test unsafe fn testRegionalCastForgery() throws (testing::TestError) { |
| 1272 | for program in [ |
| 1273 | "unsafe fn f 'r (p: *unsafe opaque) -> *unsafe Session 'r { return p as *unsafe Session 'r; }", |
| 1274 | "unsafe fn f 'r (p: *unsafe [opaque]) -> *unsafe [Session 'r] { return p as *unsafe [Session 'r]; }", |
| 1275 | "record R: 'r { s: Session 'r } unsafe fn f 'r (p: *unsafe opaque) -> *unsafe R 'r { return p as *unsafe R 'r; }", |
| 1276 | "record R: 'r + Copy { p: &'r u32 } unsafe fn f 'r 's (p: *unsafe R 'r) -> *unsafe R 's { return p as *unsafe R 's; }", |
| 1277 | ] { |
| 1278 | let mut testArena49 = super::testArena(); |
| 1279 | let testStorage49: 'test49 = &mut testArena49 in { |
| 1280 | let mut res = super::testResolver(testStorage49); |
| 1281 | let result = try super::resolveProgramStr(&mut res, program); |
| 1282 | let error = try super::expectError(&result); |
| 1283 | let case resolver::ErrorKind::InvalidAsCast(_) = error.kind else throw testing::TestError::Failed; |
| 1284 | } |
| 1285 | } |
| 1286 | } |
| 1287 | |
| 1288 | /// Bulk allocation rejects resources and values that need ownership cleanup. |
| 1289 | @test unsafe fn testSessionAllocationValue() throws (testing::TestError) { |
| 1290 | for program in [ |
| 1291 | "record R: Once { n: u32 } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }", |
| 1292 | "record R: Once { n: u32 } unsafe fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }", |
| 1293 | "record R: Once { n: u32 } record Outer { value: R } fn f 'r (s: &Session 'r, value: Outer) { try! s.new(value); }", |
| 1294 | "record R: Once { n: u32 } fn f 'r (s: &Session 'r, value: ?R) { try! s.new(value); }", |
| 1295 | "fn f 'r (s: &Session 'r, value: Session 'r) { try! s.new(value); }", |
| 1296 | "record R: Once { n: u32 } fn f 'r (s: &Session 'r, value: [R; 1]) { try! s.new(value); }", |
| 1297 | "fn f 'r (s: &Session 'r, value: &'r mut u32) { try! s.fill(value, 2); }", |
| 1298 | "fn f 'r (s: &Session 'r, value: &[&'r mut u32]) { try! s.copy(value); }", |
| 1299 | ] { |
| 1300 | let mut testArena50 = super::testArena(); |
| 1301 | let testStorage50: 'test50 = &mut testArena50 in { |
| 1302 | let mut res = super::testResolver(testStorage50); |
| 1303 | let result = try super::resolveSessionProgramStr(&mut res, program); |
| 1304 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidAllocationValue); |
| 1305 | } |
| 1306 | } |
| 1307 | } |
| 1308 | |
| 1309 | /// Allocated values must retain dependencies that cover the destination session. |
| 1310 | @test unsafe fn testSessionAllocationDependency() throws (testing::TestError) { |
| 1311 | for program in [ |
| 1312 | "fn f 'r 's (a: &Session 'r, p: &'s u32) where 'r: 's { try! a.new(p); }", |
| 1313 | "unsafe fn f 'r 's (a: &Session 'r, p: &'s u32) where 'r: 's { try! a.new(p); }", |
| 1314 | "record R: 's + Copy { p: &'s u32 } fn f 'r 's (a: &Session 'r, p: R 's) where 'r: 's { try! a.new(p); }", |
| 1315 | "fn f 'r 's (a: &Session 'r, p: &[&'s u32]) where 'r: 's { try! a.copy(p); }", |
| 1316 | ] { |
| 1317 | let mut testArena51 = super::testArena(); |
| 1318 | let testStorage51: 'test51 = &mut testArena51 in { |
| 1319 | let mut res = super::testResolver(testStorage51); |
| 1320 | let result = try super::resolveSessionProgramStr(&mut res, program); |
| 1321 | let error = try super::expectError(&result); |
| 1322 | let case resolver::ErrorKind::RegionEscape(_) = error.kind else throw testing::TestError::Failed; |
| 1323 | } |
| 1324 | } |
| 1325 | } |
| 1326 | |
| 1327 | /// Allocation operations require error handling and exact source region results. |
| 1328 | @test unsafe fn testSessionAllocationContract() throws (testing::TestError) { |
| 1329 | { |
| 1330 | let mut testArena52 = super::testArena(); |
| 1331 | let testStorage52: 'test52 = &mut testArena52 in { |
| 1332 | let mut res = super::testResolver(testStorage52); |
| 1333 | let result = try super::resolveSessionProgramStr(&mut res, |
| 1334 | "fn f 'r (s: &Session 'r) { s.new(1 as u32); }"); |
| 1335 | try super::expectErrorKind(&result, resolver::ErrorKind::MissingTry); |
| 1336 | } |
| 1337 | } |
| 1338 | { |
| 1339 | let mut testArena53 = super::testArena(); |
| 1340 | let testStorage53: 'test53 = &mut testArena53 in { |
| 1341 | let mut res = super::testResolver(testStorage53); |
| 1342 | let result = try super::resolveSessionProgramStr(&mut res, |
| 1343 | "fn f 'r 's (a: &Session 'r) -> &'s mut u32 { return try! a.new(1 as u32); }"); |
| 1344 | let error = try super::expectError(&result); |
| 1345 | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
| 1346 | } |
| 1347 | } |
| 1348 | { |
| 1349 | let mut testArena54 = super::testArena(); |
| 1350 | let testStorage54: 'test54 = &mut testArena54 in { |
| 1351 | let mut res = super::testResolver(testStorage54); |
| 1352 | let result = try super::resolveSessionProgramStr(&mut res, |
| 1353 | "fn f 'r (a: &Session 'r, p: &u32) { try! a.new(p); }"); |
| 1354 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidRefPosition); |
| 1355 | } |
| 1356 | } |
| 1357 | } |
| 1358 | |
| 1359 | /// Typed reservations reject array products, aggregate sums, and field-offset overflow. |
| 1360 | @test unsafe fn testSessionAllocationLayout() throws (testing::TestError) { |
| 1361 | for program in [ |
| 1362 | "fn f 'r (s: &Session 'r, value: [u64; 536870913]) { try! s.new(value); }", |
| 1363 | "unsafe fn f 'r (s: &Session 'r, value: [u64; 536870913]) { try! s.new(value); }", |
| 1364 | "record R { a: [u8; 4294967290], b: u64 } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }", |
| 1365 | "record R { a: [u8; 2147483648], b: u8 } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }", |
| 1366 | "fn f 'r (s: &Session 'r, value: ?[u8; 4294967295]) { try! s.new(value); }", |
| 1367 | "union R { Data([u8; 4294967295]) } fn f 'r (s: &Session 'r, value: R) { try! s.new(value); }", |
| 1368 | ] { |
| 1369 | let mut testArena55 = super::testArena(); |
| 1370 | let testStorage55: 'test55 = &mut testArena55 in { |
| 1371 | let mut res = super::testResolver(testStorage55); |
| 1372 | let result = try super::resolveSessionProgramStr(&mut res, program); |
| 1373 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidAllocationLayout); |
| 1374 | } |
| 1375 | } |
| 1376 | } |
| 1377 | |
| 1378 | /// Conditional bindings enforce moves through bodies and guards. |
| 1379 | @test unsafe fn testConditionalBindingMoves() throws (testing::TestError) { |
| 1380 | for program in [ |
| 1381 | "record Token {} fn take(value: Token) {} fn f(value: ?Token) { if let item = value { take(item); take(item); } }", |
| 1382 | "record Token {} fn take(value: Token) {} fn f(value: ?Token) { let item = value else return; take(item); take(item); }", |
| 1383 | "record Token {} fn take(value: Token) {} fn next() -> ?Token { return Token {}; } fn f() { while let item = next() { take(item); take(item); } }", |
| 1384 | "record Token {} fn take(value: Token) -> bool { return true; } fn f(value: ?Token) { if let item = value; take(item) { take(item); } }", |
| 1385 | "record Token {} fn take(value: Token) {} unsafe fn f(value: ?Token) { if let item = value { take(item); take(item); } }", |
| 1386 | "record Token {} record Box { item: Token } fn take(item: Token) -> bool { return true; } fn f(box: Box) { let case Box { item } = box if take(item) else return; take(item); }", |
| 1387 | "record Token {} record Box { item: Token } fn take(item: Token) -> bool { return true; } unsafe fn f(box: Box) { let case Box { item } = box if take(item) else return; take(item); }", |
| 1388 | "record Token {} union Box { Full(Token), Empty } fn take(item: Token) -> bool { return true; } fn f(box: Box) { let case Box::Full(item) = box if take(item) else return; take(item); }", |
| 1389 | ] { |
| 1390 | let mut storage = super::testArena(); |
| 1391 | let memory: 'test = &mut storage in { |
| 1392 | let mut res = super::testResolver(memory); |
| 1393 | let result = try super::resolveProgramStr(&mut res, program); |
| 1394 | try super::expectErrorKind(&result, resolver::ErrorKind::AffineUseAfterMove("item")); |
| 1395 | } |
| 1396 | } |
| 1397 | } |
| 1398 | |
| 1399 | /// Guard ownership distinguishes consumed fields, borrowed fields, and failure paths. |
| 1400 | @test unsafe fn testLetElseGuardOwnership() throws (testing::TestError) { |
| 1401 | for program in [ |
| 1402 | "record Token {} record Box { item: Token } fn take(item: Token) -> bool { return true; } fn f(box: Box) { let case Box { item } = box if take(item) else return; }", |
| 1403 | "record Token {} record Box { item: Token } fn read(item: &Token) -> bool { return true; } fn take(item: Token) {} fn f(box: Box) { let case Box { item } = box if read(&item) else return; take(item); }", |
| 1404 | "union Box { Full(u32), Empty } fn f(box: Box) -> u32 { let case Box::Full(item) = box if item > 0 else return 0; return item; }", |
| 1405 | ] { |
| 1406 | let mut storage = super::testArena(); |
| 1407 | let memory: 'test = &mut storage in { |
| 1408 | let mut res = super::testResolver(memory); |
| 1409 | let result = try super::resolveProgramStr(&mut res, program); |
| 1410 | try super::expectNoErrors(&result); |
| 1411 | } |
| 1412 | } |
| 1413 | let mut storage = super::testArena(); |
| 1414 | let memory: 'test = &mut storage in { |
| 1415 | let mut res = super::testResolver(memory); |
| 1416 | let result = try super::resolveProgramStr(&mut res, |
| 1417 | "record Token {} record Box { item: u32 } fn take(outer: Token) -> bool { return false; } fn f(box: Box, outer: Token) { let case Box { item } = box if take(outer) else { take(outer); return; }; }"); |
| 1418 | try super::expectErrorKind(&result, resolver::ErrorKind::AffineUseAfterMove("outer")); |
| 1419 | } |
| 1420 | } |
| 1421 | |
| 1422 | /// Fallback expressions cannot access their new binding before initialization. |
| 1423 | @test unsafe fn testLetElseFallbackBinding() throws (testing::TestError) { |
| 1424 | for program in [ |
| 1425 | "fn f(input: ?u32) -> u32 { let value = input else value; return value; }", |
| 1426 | "unsafe fn f(input: ?u32) -> u32 { let value = input else value; return value; }", |
| 1427 | "fn f(input: ?u32) -> u32 { let mut value = input else { value }; return value; }", |
| 1428 | ] { |
| 1429 | let mut storage = super::testArena(); |
| 1430 | let memory: 'test = &mut storage in { |
| 1431 | let mut res = super::testResolver(memory); |
| 1432 | let result = try super::resolveProgramStr(&mut res, program); |
| 1433 | try super::expectErrorKind(&result, resolver::ErrorKind::UnresolvedSymbol("value")); |
| 1434 | } |
| 1435 | } |
| 1436 | } |
| 1437 | |
| 1438 | /// Each successful session allocation has distinct storage across loop iterations. |
| 1439 | @test unsafe fn testFreshAllocationLoopLoan() throws (testing::TestError) { |
| 1440 | for program in [ |
| 1441 | "fn f 'r (s: &Session 'r) { for i in 0..3 { let p = try! s.fill(0 as u32, 2); set p[0] = i; let view: &'r [u32] = &p[..]; } }", |
| 1442 | "fn f 'r (s: &Session 'r) { let mut i: u32 = 0; while i < 3 { let p = try! s.new(0 as u32); set *p = i; let view: &'r u32 = &*p; set i += 1; } }", |
| 1443 | "fn f 'r (s: &Session 'r) { for i in 0..3 { if let p = try? s.fill(0 as u32, 2); p.len == 2 { set p[0] = i; let view: &'r [u32] = &p[..]; } } }", |
| 1444 | "fn f 'r (s: &Session 'r) { while let p = try? s.new(0 as u32) { set *p = 1; let view: &'r u32 = &*p; } }", |
| 1445 | "fn f 'r (s: &Session 'r) { loop { let p = try? s.new(0 as u32) else break; set *p = 1; let view: &'r u32 = &*p; } }", |
| 1446 | "fn f 'r (s: &Session 'r) { for i in 0..3 { if let _ = try? s.new(0 as u32) {} let _ = try! s.new(0 as u32); } }", |
| 1447 | ] { |
| 1448 | let mut storage = super::testArena(); |
| 1449 | let memory: 'test = &mut storage in { |
| 1450 | let mut res = super::testResolver(memory); |
| 1451 | let result = try super::resolveSessionProgramStr(&mut res, program); |
| 1452 | try super::expectNoErrors(&result); |
| 1453 | } |
| 1454 | } |
| 1455 | } |
| 1456 | |
| 1457 | /// Fresh allocations preserve loans on aliases and existing storage. |
| 1458 | @test unsafe fn testFreshAllocationRetainsOtherLoans() throws (testing::TestError) { |
| 1459 | for program in [ |
| 1460 | "fn f 'r (s: &Session 'r) { for i in 0..3 { let p = try! s.fill(0 as u32, 2); let view: &'r [u32] = &p[..]; set p[0] = i; } }", |
| 1461 | "fn f 'r (s: &Session 'r) { let p = try! s.fill(0 as u32, 2); for i in 0..3 { set p[0] = i; let view: &'r [u32] = &p[..]; } }", |
| 1462 | "fn f 'r (s: &Session 'r, p: &'r mut u32) { for i in 0..3 { let view: &'r u32 = &*p; let fresh = try! s.new(0 as u32); set *p = i; } }", |
| 1463 | "fn f 'r (s: &Session 'r, p: &'r mut u32) { for i in 0..3 { let fresh = try! s.new(&*p); set *p = i; } }", |
| 1464 | "fn f 'r (s: &Session 'r) { while let p = try? s.new(0 as u32) { let view: &'r u32 = &*p; set *p = 1; } }", |
| 1465 | "fn f 'r (s: &Session 'r) { for i in 0..3 { if let p = try? s.new(0 as u32) { let view: &'r u32 = &*p; set *p = i; } } }", |
| 1466 | "fn f 'r (s: &Session 'r) { loop { let p = try? s.new(0 as u32) else break; let view: &'r u32 = &*p; set *p = 1; } }", |
| 1467 | "fn check 'r (p: &'r u32) -> bool { return *p == 0; } fn f 'r (s: &Session 'r) { while let p = try? s.new(0 as u32); check(&*p) { set *p = 1; } }", |
| 1468 | ] { |
| 1469 | let mut storage = super::testArena(); |
| 1470 | let memory: 'test = &mut storage in { |
| 1471 | let mut res = super::testResolver(memory); |
| 1472 | let result = try super::resolveSessionProgramStr(&mut res, program); |
| 1473 | try super::expectErrorKind(&result, resolver::ErrorKind::BorrowConflict("p")); |
| 1474 | } |
| 1475 | } |
| 1476 | } |
| 1477 | |
| 1478 | /// Cell references give indirect cycles finite layouts in each declaration order. |
| 1479 | @test unsafe fn testRecursiveCellLayout() throws (testing::TestError) { |
| 1480 | for program in [ |
| 1481 | "record V: 'r + Copy { n: N 'r } record N: 'r + Copy { s: &'r cell S 'r } union S: 'r + Copy { Ready(V 'r), Pending }", |
| 1482 | "union S: 'r + Copy { Ready(V 'r), Pending } record V: 'r + Copy { n: N 'r } record N: 'r + Copy { s: &'r cell S 'r }", |
| 1483 | "record N: 'r + Copy { s: &'r cell S 'r } union S: 'r + Copy { Ready(V 'r), Pending } record V: 'r + Copy { n: N 'r }", |
| 1484 | "record V: Copy { n: N } record N: Copy { s: *cell S } union S: Copy { Ready(V), Pending }", |
| 1485 | ] { |
| 1486 | let mut storage = super::testArena(); |
| 1487 | let memory: 'test = &mut storage in { |
| 1488 | let mut res = super::testResolver(memory); |
| 1489 | let result = try super::resolveProgramStr(&mut res, program); |
| 1490 | try super::expectNoErrors(&result); |
| 1491 | } |
| 1492 | } |
| 1493 | } |
| 1494 | |
| 1495 | /// Deferred cell checks reject invalid payloads without a payload access. |
| 1496 | @test unsafe fn testDeferredCellPayload() throws (testing::TestError) { |
| 1497 | for program in [ |
| 1498 | "record N: 'r + Copy { s: &'r cell S 'r } union S: 'r { Ready(N 'r), Pending }", |
| 1499 | "record N: Copy { s: *cell S } record S { value: u32 }", |
| 1500 | ] { |
| 1501 | let mut storage = super::testArena(); |
| 1502 | let memory: 'test = &mut storage in { |
| 1503 | let mut res = super::testResolver(memory); |
| 1504 | let result = try super::resolveProgramStr(&mut res, program); |
| 1505 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidCellPayload); |
| 1506 | } |
| 1507 | } |
| 1508 | let mut storage = super::testArena(); |
| 1509 | let memory: 'test = &mut storage in { |
| 1510 | let mut res = super::testResolver(memory); |
| 1511 | let result = try super::resolveProgramStr(&mut res, |
| 1512 | "record N: Copy { s: *cell S } record S: Copy { bytes: [u64; 536870913] }"); |
| 1513 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidAllocationLayout); |
| 1514 | } |
| 1515 | } |
| 1516 | |
| 1517 | /// Cell payloads must permit plain value copies. |
| 1518 | @test unsafe fn testCellPointerPayload() throws (testing::TestError) { |
| 1519 | for program in [ |
| 1520 | "union State: 'r { Next(&'r cell State 'r) }", |
| 1521 | "union State: 'r { Ready(&'r u32) } fn f 'r (state: &'r cell State 'r) {}", |
| 1522 | "union State: 'r { Ready(&'r u32) } unsafe fn f 'r (state: &'r cell State 'r) {}", |
| 1523 | "fn f(p: *cell *mut u32) {}", |
| 1524 | "unsafe fn f(p: *cell *mut u32) {}", |
| 1525 | "record R: Once { n: u32 } fn f(p: *cell R) {}", |
| 1526 | "record R { n: u32 } fn f(p: *cell R) {}", |
| 1527 | "fn f 'r (p: &'r cell &'r mut u32) {}", |
| 1528 | ] { |
| 1529 | let mut testArena56 = super::testArena(); |
| 1530 | let testStorage56: 'test56 = &mut testArena56 in { |
| 1531 | let mut res = super::testResolver(testStorage56); |
| 1532 | let result = try super::resolveProgramStr(&mut res, program); |
| 1533 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidCellPayload); |
| 1534 | } |
| 1535 | } |
| 1536 | } |
| 1537 | |
| 1538 | /// Direct cell borrows support Copy values, scoped loans, and call arguments. |
| 1539 | @test unsafe fn testCellBorrow() throws (testing::TestError) { |
| 1540 | for program in [ |
| 1541 | "fn f() -> u32 { let mut n: u32 = 0; { let c = &cell n; let alias = c; set *c = 1; set *alias += 1; } return n; }", |
| 1542 | "fn read(c: &cell u32) { set *c = 1; } fn f(p: &mut u32) { read(&cell *p); set *p = 2; }", |
| 1543 | "fn f() { let mut n: u32 = 0; let c: 'r = &cell n in { let alias = c; set *alias = 1; } set n = 2; }", |
| 1544 | "record R: Copy { a: u32, b: u32 } fn f() { let mut r = R { a: 0, b: 0 }; let a = &cell r.a; let b = &cell r.b; set *a = 1; set *b = 2; }", |
| 1545 | "fn f() { let mut a: [u32; 2] = [0, 0]; let c = &cell a; set *c = [1, 2]; let snapshot = *c; }", |
| 1546 | "fn forward(p: *mut u32) -> *mut u32 { return p; } fn f(p: *mut u32) -> *cell u32 { return &cell *forward(p); }", |
| 1547 | "record R: Copy { n: u32 } fn f(p: *mut R) -> *cell u32 { return &cell p.n; }", |
| 1548 | "fn f(p: *mut [u32]) -> *cell u32 { return &cell p[0]; }", |
| 1549 | ] { |
| 1550 | let mut arena = super::testArena(); |
| 1551 | let storage: 'test = &mut arena in { |
| 1552 | let mut res = super::testResolver(storage); |
| 1553 | let result = try super::resolveProgramStr(&mut res, program); |
| 1554 | try super::expectNoErrors(&result); |
| 1555 | } |
| 1556 | } |
| 1557 | } |
| 1558 | |
| 1559 | /// Permission-associated cells support construction, direct access, and sequential mutation. |
| 1560 | @test unsafe fn testPermissionCellConstruction() throws (testing::TestError) { |
| 1561 | for program in [ |
| 1562 | // Initialized exclusive Copy storage construction. |
| 1563 | "record Permit {} fn f() { let mut owner = Permit {}; let permission: 'permission = &mut owner in { let mut value: u32 = 0; let cell: &cell 'permission u32 = &cell 'permission value; set *cell = 1; let copy = *cell; } }", |
| 1564 | // Sequential mutation after each previous access has ended. |
| 1565 | "record Permit {} fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { set *cell = 1; let first = *cell; set *cell = first + 1; let second = *cell; }", |
| 1566 | ] { |
| 1567 | let mut arena = super::testArena(); |
| 1568 | let storage: 'test = &mut arena in { |
| 1569 | let mut res = super::testResolver(storage); |
| 1570 | let result = try super::resolveProgramStr(&mut res, program); |
| 1571 | try super::expectNoErrors(&result); |
| 1572 | } |
| 1573 | } |
| 1574 | } |
| 1575 | |
| 1576 | /// Associated cell construction requires active authority for its permission region. |
| 1577 | @test unsafe fn testPermissionCellConstructionAuthority() throws (testing::TestError) { |
| 1578 | let mut arena = super::testArena(); |
| 1579 | let storage: 'test = &mut arena in { |
| 1580 | let mut res = super::testResolver(storage); |
| 1581 | let result = try super::resolveProgramStr(&mut res, |
| 1582 | "record Permit {} fn f 'permission (place: &mut u32, owner: Permit) { let cell: &cell 'permission u32 = &cell 'permission *place; }"); |
| 1583 | let error = try super::expectError(&result); |
| 1584 | let case resolver::ErrorKind::CellPermissionRequired(_) = error.kind |
| 1585 | else throw testing::TestError::Failed; |
| 1586 | } |
| 1587 | } |
| 1588 | |
| 1589 | /// Matching authority permits shared and mutable payload loans and ordinary helper calls. |
| 1590 | @test unsafe fn testPermissionCellPayloadLoans() throws (testing::TestError) { |
| 1591 | for program in [ |
| 1592 | // A shared payload reference can be passed to an ordinary helper. |
| 1593 | "record Permit {} fn observe(value: &u32) {} fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let authority: 'loan = &*permission, payload = &*cell in { observe(payload); } }", |
| 1594 | // A mutable payload reference can be passed to an ordinary `&mut` helper. |
| 1595 | "record Permit {} fn increment(value: &mut u32) { set *value += 1; } fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let authority: 'loan = &mut *permission, payload = &mut *cell in { increment(payload); } }", |
| 1596 | // Cell aliases retain authority and may create multiple simultaneous readers. |
| 1597 | "record Permit {} fn observe(value: &u32) {} fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let alias = cell; let authority: 'loan = &*permission, first = &*cell, second = &*alias in { observe(first); observe(second); } }", |
| 1598 | ] { |
| 1599 | let mut arena = super::testArena(); |
| 1600 | let storage: 'test = &mut arena in { |
| 1601 | let mut res = super::testResolver(storage); |
| 1602 | let result = try super::resolveProgramStr(&mut res, program); |
| 1603 | try super::expectNoErrors(&result); |
| 1604 | } |
| 1605 | } |
| 1606 | } |
| 1607 | |
| 1608 | /// Aliasing, generic substitution, and storage shortening preserve the permission association. |
| 1609 | @test unsafe fn testPermissionCellSubstitution() throws (testing::TestError) { |
| 1610 | for program in [ |
| 1611 | // An inferred generic call substitutes both storage and permission regions. |
| 1612 | "record Permit {} fn identity 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) -> &'storage cell 'permission u32 { return cell; } fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let alias = cell; let instantiated = identity(alias, permission); }", |
| 1613 | // Shortening storage leaves the independent permission region unchanged. |
| 1614 | "record Permit {} fn shorten 'storage 'short 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) -> &'short cell 'permission u32 where 'storage: 'short { return cell; }", |
| 1615 | ] { |
| 1616 | let mut arena = super::testArena(); |
| 1617 | let storage: 'test = &mut arena in { |
| 1618 | let mut res = super::testResolver(storage); |
| 1619 | let result = try super::resolveProgramStr(&mut res, program); |
| 1620 | try super::expectNoErrors(&result); |
| 1621 | } |
| 1622 | } |
| 1623 | } |
| 1624 | |
| 1625 | /// Calls may reuse authority when a helper's permission contract does not return it. |
| 1626 | @test unsafe fn testPermissionCellAuthorityCallReuse() throws (testing::TestError) { |
| 1627 | { |
| 1628 | let mut arena = super::testArena(); |
| 1629 | let storage: 'test = &mut arena in { |
| 1630 | let mut res = super::testResolver(storage); |
| 1631 | let result = try super::resolveProgramStr(&mut res, |
| 1632 | "record Permit {} fn inspect 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) -> u32 { return *cell; } fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let first = inspect(cell, permission); let second = inspect(cell, permission); }"); |
| 1633 | try super::expectNoErrors(&result); |
| 1634 | } |
| 1635 | } |
| 1636 | { |
| 1637 | let mut arena = super::testArena(); |
| 1638 | let storage: 'test = &mut arena in { |
| 1639 | let mut res = super::testResolver(storage); |
| 1640 | let result = try super::resolveProgramStr(&mut res, |
| 1641 | "record Permit {} fn retain 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) -> &'permission mut Permit { let value = *cell; return permission; } fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let retained = retain(cell, permission); retain(cell, permission); }"); |
| 1642 | let error = try super::expectError(&result); |
| 1643 | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind |
| 1644 | else throw testing::TestError::Failed; |
| 1645 | } |
| 1646 | } |
| 1647 | { |
| 1648 | let mut arena = super::testArena(); |
| 1649 | let storage: 'test = &mut arena in { |
| 1650 | let mut res = super::testResolver(storage); |
| 1651 | let result = try super::resolveProgramStr(&mut res, |
| 1652 | "record Permit {} fn retainIn 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit, out: &mut ?&'permission mut Permit) { let value = *cell; set *out = permission; } fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit, firstOut: &mut ?&'permission mut Permit, secondOut: &mut ?&'permission mut Permit) { retainIn(cell, permission, firstOut); retainIn(cell, permission, secondOut); }"); |
| 1653 | let error = try super::expectError(&result); |
| 1654 | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind |
| 1655 | else throw testing::TestError::Failed; |
| 1656 | } |
| 1657 | } |
| 1658 | { |
| 1659 | let mut arena = super::testArena(); |
| 1660 | let storage: 'test = &mut arena in { |
| 1661 | let mut res = super::testResolver(storage); |
| 1662 | let result = try super::resolveProgramStr(&mut res, |
| 1663 | "record Permit {} fn inspect 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) -> u32 { return *cell; } fn retainIn 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit, storage: &'permission mut ?&'permission mut Permit) { let value = *cell; set *storage = permission; } fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit, authorityStorage: &'permission mut ?&'permission mut Permit) { retainIn(cell, permission, authorityStorage); inspect(cell, permission); }"); |
| 1664 | let error = try super::expectError(&result); |
| 1665 | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind |
| 1666 | else throw testing::TestError::Failed; |
| 1667 | } |
| 1668 | } |
| 1669 | } |
| 1670 | |
| 1671 | /// Fresh allocation does not reclaim storage and remains valid during a shared payload loan. |
| 1672 | @test unsafe fn testPermissionCellFreshAllocation() throws (testing::TestError) { |
| 1673 | let mut arena = super::testArena(); |
| 1674 | let storage: 'test = &mut arena in { |
| 1675 | let mut res = super::testResolver(storage); |
| 1676 | let result = try super::resolveSessionProgramStr(&mut res, |
| 1677 | "record Permit {} fn f 'storage 'permission (session: &Session 'storage, cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let authority: 'loan = &*permission, payload = &*cell in { let fresh = try! session.new(2 as u32); set *fresh = *payload; } }"); |
| 1678 | try super::expectNoErrors(&result); |
| 1679 | } |
| 1680 | } |
| 1681 | |
| 1682 | /// Cell permission metadata does not make a session allocation depend on shorter authority. |
| 1683 | @test unsafe fn testPermissionCellSessionNominalDependency() throws (testing::TestError) { |
| 1684 | let mut arena = super::testArena(); |
| 1685 | let storage: 'test = &mut arena in { |
| 1686 | let mut res = super::testResolver(storage); |
| 1687 | let result = try super::resolveSessionProgramStr(&mut res, |
| 1688 | "record Permit {} record Block: 'storage + 'permission + Copy { cell: &'storage cell 'permission u32 } fn f 'storage (session: &Session 'storage) { let mut owner = Permit {}; let permission: 'permission = &mut owner in { let place = try! session.new(1 as u32); let cell: &'storage cell 'permission u32 = &cell 'permission *place; let block = try! session.new(Block 'storage 'permission { cell }); } }"); |
| 1689 | try super::expectNoErrors(&result); |
| 1690 | } |
| 1691 | } |
| 1692 | |
| 1693 | /// Associated Copy cells can form finite-layout cycles under one permission. |
| 1694 | @test unsafe fn testPermissionCellCycles() throws (testing::TestError) { |
| 1695 | let mut arena = super::testArena(); |
| 1696 | let storage: 'test = &mut arena in { |
| 1697 | let mut res = super::testResolver(storage); |
| 1698 | let result = try super::resolveProgramStr(&mut res, |
| 1699 | "record Left: 'storage + 'permission + Copy { right: &'storage cell 'permission Right 'storage 'permission } record Right: 'storage + 'permission + Copy { left: &'storage cell 'permission Left 'storage 'permission }"); |
| 1700 | try super::expectNoErrors(&result); |
| 1701 | } |
| 1702 | } |
| 1703 | |
| 1704 | /// Owned cell payloads ignore nested permission-only metadata but retain storage dependencies. |
| 1705 | @test unsafe fn testPermissionCellOwnedNestedPayloadRegions() throws (testing::TestError) { |
| 1706 | { |
| 1707 | let mut arena = super::testArena(); |
| 1708 | let storage: 'test = &mut arena in { |
| 1709 | let mut res = super::testResolver(storage); |
| 1710 | let result = try super::resolveProgramStr(&mut res, |
| 1711 | "record Permit {} record Payload: 'permission + Copy { nested: *cell 'permission u32 } fn f 'permission (place: *mut Payload 'permission, permission: &'permission mut Permit) -> *cell Payload 'permission { return &cell *place; }"); |
| 1712 | try super::expectNoErrors(&result); |
| 1713 | } |
| 1714 | } |
| 1715 | { |
| 1716 | let mut arena = super::testArena(); |
| 1717 | let storage: 'test = &mut arena in { |
| 1718 | let mut res = super::testResolver(storage); |
| 1719 | let result = try super::resolveProgramStr(&mut res, |
| 1720 | "record Payload: 'storage + Copy { nested: &'storage cell u32 } fn f 'storage (place: *mut Payload 'storage) -> *cell Payload 'storage { return &cell *place; }"); |
| 1721 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidCellPayload); |
| 1722 | } |
| 1723 | } |
| 1724 | } |
| 1725 | |
| 1726 | /// Resolved cell metadata keeps storage and permission identities distinct through substitution. |
| 1727 | @test unsafe fn testPermissionCellTypeMetadata() throws (testing::TestError) { |
| 1728 | let mut arena = super::testArena(); |
| 1729 | let storage: 'test = &mut arena in { |
| 1730 | let mut res = super::testResolver(storage); |
| 1731 | let result = try super::resolveProgramStr(&mut res, |
| 1732 | "record Permit {} fn identity 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) -> &'storage cell 'permission u32 { return cell; } fn inspect 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let alias = cell; let instantiated = identity(alias, permission); }"); |
| 1733 | try super::expectNoErrors(&result); |
| 1734 | |
| 1735 | let inspect = try super::getBlockStmt(result.root, 2); |
| 1736 | let inspectType = resolver::typeFor(&res, inspect) else throw testing::TestError::Failed; |
| 1737 | let case resolver::Type::Fn(info) = inspectType else throw testing::TestError::Failed; |
| 1738 | let case resolver::Type::Cell { |
| 1739 | class: types::PointerClass::Region(storageRegion), |
| 1740 | permission: declaredPermission, |
| 1741 | payload: declaredPayload, |
| 1742 | } = *info.paramTypes[0] else throw testing::TestError::Failed; |
| 1743 | let permissionRegion = declaredPermission else throw testing::TestError::Failed; |
| 1744 | let case resolver::Type::Pointer { |
| 1745 | class: types::PointerClass::Region(authorityRegion), |
| 1746 | target: _, |
| 1747 | mutable: true, |
| 1748 | } = *info.paramTypes[1] else throw testing::TestError::Failed; |
| 1749 | try testing::expect(storageRegion.id <> permissionRegion.id); |
| 1750 | try testing::expect(authorityRegion.id == permissionRegion.id); |
| 1751 | try testing::expect(*declaredPayload == resolver::Type::U32); |
| 1752 | |
| 1753 | let case ast::NodeValue::FnDecl(decl) = inspect.value else throw testing::TestError::Failed; |
| 1754 | let body = decl.body else throw testing::TestError::Failed; |
| 1755 | let case ast::NodeValue::Let(aliasDecl) = |
| 1756 | (try super::getBlockStmt(body, 0)).value else throw testing::TestError::Failed; |
| 1757 | let case ast::NodeValue::Let(instantiatedDecl) = |
| 1758 | (try super::getBlockStmt(body, 1)).value else throw testing::TestError::Failed; |
| 1759 | let aliasType = resolver::typeFor(&res, aliasDecl.value) else throw testing::TestError::Failed; |
| 1760 | let instantiatedType = resolver::typeFor(&res, instantiatedDecl.value) |
| 1761 | else throw testing::TestError::Failed; |
| 1762 | let case resolver::Type::Cell { |
| 1763 | class: types::PointerClass::Region(aliasStorage), |
| 1764 | permission: aliasPermission, |
| 1765 | .. |
| 1766 | } = aliasType else throw testing::TestError::Failed; |
| 1767 | let case resolver::Type::Cell { |
| 1768 | class: types::PointerClass::Region(instantiatedStorage), |
| 1769 | permission: instantiatedPermission, |
| 1770 | .. |
| 1771 | } = instantiatedType else throw testing::TestError::Failed; |
| 1772 | let aliasPermissionRegion = aliasPermission else throw testing::TestError::Failed; |
| 1773 | let instantiatedPermissionRegion = instantiatedPermission else throw testing::TestError::Failed; |
| 1774 | try testing::expect(aliasStorage.id == storageRegion.id); |
| 1775 | try testing::expect(instantiatedStorage.id == storageRegion.id); |
| 1776 | try testing::expect(aliasPermissionRegion.id == permissionRegion.id); |
| 1777 | try testing::expect(instantiatedPermissionRegion.id == permissionRegion.id); |
| 1778 | } |
| 1779 | } |
| 1780 | |
| 1781 | /// Permission-wide loans reject conflicting accesses through every cell alias. |
| 1782 | @test unsafe fn testPermissionCellAliasConflicts() throws (testing::TestError) { |
| 1783 | for program in [ |
| 1784 | // Two aliases cannot produce overlapping mutable payload references. |
| 1785 | "record Permit {} fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let alias = cell; let authority: 'loan = &mut *permission, first = &mut *cell, second = &mut *alias in {} }", |
| 1786 | // A shared payload loan suspends mutation through another alias. |
| 1787 | "record Permit {} fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let alias = cell; let authority: 'loan = &*permission, payload = &*cell in { set *alias = 1; let value = *payload; } }", |
| 1788 | ] { |
| 1789 | let mut arena = super::testArena(); |
| 1790 | let storage: 'test = &mut arena in { |
| 1791 | let mut res = super::testResolver(storage); |
| 1792 | let result = try super::resolveProgramStr(&mut res, program); |
| 1793 | let error = try super::expectError(&result); |
| 1794 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind |
| 1795 | else throw testing::TestError::Failed; |
| 1796 | } |
| 1797 | } |
| 1798 | } |
| 1799 | /// Call argument loans preserve permission identity across distinct cell handles. |
| 1800 | @test unsafe fn testPermissionCellCallArgumentAliasConflict() throws (testing::TestError) { |
| 1801 | let mut arena = super::testArena(); |
| 1802 | let storage: 'test = &mut arena in { |
| 1803 | let mut res = super::testResolver(storage); |
| 1804 | let result = try super::resolveProgramStr(&mut res, |
| 1805 | "record Permit {} fn both(first: &u32, second: &mut u32) {} fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let alias = cell; let authority: 'loan = &mut *permission where 'permission: 'loan in { both(&*cell, &mut *alias); } }"); |
| 1806 | try super::expectErrorKind(&result, resolver::ErrorKind::BorrowConflict("alias")); |
| 1807 | } |
| 1808 | } |
| 1809 | |
| 1810 | /// Wrapped local payload loans preserve permission identity. |
| 1811 | @test unsafe fn testPermissionCellWrappedLocalLoanConflict() throws (testing::TestError) { |
| 1812 | let mut arena = super::testArena(); |
| 1813 | let storage: 'test = &mut arena in { |
| 1814 | let mut res = super::testResolver(storage); |
| 1815 | let result = try super::resolveProgramStr(&mut res, |
| 1816 | "record Permit {} fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let alias = cell; let authority: 'loan = &*permission where 'permission: 'loan in { let payload = &*cell as &u32; set *alias = 1; let value = *payload; } }"); |
| 1817 | let error = try super::expectError(&result); |
| 1818 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind |
| 1819 | else throw testing::TestError::Failed; |
| 1820 | } |
| 1821 | } |
| 1822 | /// Sessions nested in cell payloads retain consumed permission authority. |
| 1823 | @test unsafe fn testPermissionCellNestedSessionRetention() throws (testing::TestError) { |
| 1824 | let mut arena = super::testArena(); |
| 1825 | let storage: 'test = &mut arena in { |
| 1826 | let mut res = super::testResolver(storage); |
| 1827 | let result = try super::resolveSessionProgramStr(&mut res, |
| 1828 | "record Permit {} fn retain 'storage 'session 'permission (holder: &'storage cell 'permission &'session mut Session 'permission, permission: &'permission mut Permit) { let stored = try! (*holder).new(permission); } fn f 'storage 'session 'permission (holder: &'storage cell 'permission &'session mut Session 'permission, permission: &'permission mut Permit) { retain(holder, permission); retain(holder, permission); }"); |
| 1829 | try super::expectErrorKind( |
| 1830 | &result, resolver::ErrorKind::AffineUseAfterMove("permission"), |
| 1831 | ); |
| 1832 | } |
| 1833 | } |
| 1834 | |
| 1835 | |
| 1836 | /// Associated cell projections support replacement under matching authority. |
| 1837 | @test unsafe fn testPermissionCellProjectedReplacement() throws (testing::TestError) { |
| 1838 | for program in [ |
| 1839 | "record Permit {} record Payload { value: u32 } fn f 'storage 'permission (cell: &'storage cell 'permission Payload, permission: &'permission mut Permit) { set (*cell).value = 1; }", |
| 1840 | "record Permit {} fn f 'storage 'permission (cell: &'storage cell 'permission [u32; 2], permission: &'permission mut Permit) { set (*cell)[0] = 1; }", |
| 1841 | "record Permit {} fn f 'storage 'permission (cell: &'storage cell 'permission [u32; 2], permission: &'permission mut Permit) { set (*cell)[0..1] = 0; }", |
| 1842 | ] { |
| 1843 | let mut arena = super::testArena(); |
| 1844 | let storage: 'test = &mut arena in { |
| 1845 | let mut res = super::testResolver(storage); |
| 1846 | let result = try super::resolveProgramStr(&mut res, program); |
| 1847 | try super::expectNoErrors(&result); |
| 1848 | } |
| 1849 | } |
| 1850 | } |
| 1851 | |
| 1852 | |
| 1853 | /// Permission identities are invariant across access, relabeling, and storage shortening. |
| 1854 | @test unsafe fn testPermissionCellAssociationMismatch() throws (testing::TestError) { |
| 1855 | { |
| 1856 | let mut arena = super::testArena(); |
| 1857 | let storage: 'test = &mut arena in { |
| 1858 | let mut res = super::testResolver(storage); |
| 1859 | // Cross-permission access fails generic region inference. |
| 1860 | let result = try super::resolveProgramStr(&mut res, |
| 1861 | "record Permit {} fn access 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) -> u32 { return *cell; } fn cross 'storage 'first 'second (cell: &'storage cell 'first u32, first: &'first mut Permit, second: &'second mut Permit) { access(cell, second); }"); |
| 1862 | let error = try super::expectError(&result); |
| 1863 | let case resolver::ErrorKind::RegionInference(_) = error.kind |
| 1864 | else throw testing::TestError::Failed; |
| 1865 | } |
| 1866 | } |
| 1867 | for program in [ |
| 1868 | // An explicit annotation cannot relabel a permission. |
| 1869 | "record Permit {} fn f 'storage 'first 'second (cell: &'storage cell 'first u32, first: &'first mut Permit, second: &'second mut Permit) { let relabeled: &'storage cell 'second u32 = cell; }", |
| 1870 | // Storage shortening cannot change the permission association. |
| 1871 | "record Permit {} fn f 'storage 'short 'first 'second (cell: &'storage cell 'first u32, first: &'first mut Permit, second: &'second mut Permit) where 'storage: 'short { let relabeled: &'short cell 'second u32 = cell; }", |
| 1872 | // Storage shortening cannot drop the permission association. |
| 1873 | "record Permit {} fn f 'storage 'short 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) where 'storage: 'short { let unassociated: &'short cell u32 = cell; }", |
| 1874 | ] { |
| 1875 | let mut arena = super::testArena(); |
| 1876 | let storage: 'test = &mut arena in { |
| 1877 | let mut res = super::testResolver(storage); |
| 1878 | let result = try super::resolveProgramStr(&mut res, program); |
| 1879 | let error = try super::expectError(&result); |
| 1880 | let case resolver::ErrorKind::TypeMismatch(_) = error.kind |
| 1881 | else throw testing::TestError::Failed; |
| 1882 | } |
| 1883 | } |
| 1884 | } |
| 1885 | |
| 1886 | /// Distinct formal permissions cannot substitute the same actual permission identity. |
| 1887 | @test unsafe fn testPermissionCellDistinctGenericArguments() throws (testing::TestError) { |
| 1888 | let mut arena = super::testArena(); |
| 1889 | let storage: 'test = &mut arena in { |
| 1890 | let mut res = super::testResolver(storage); |
| 1891 | let result = try super::resolveProgramStr(&mut res, |
| 1892 | "record Permit {} fn separate 'storage 'left 'right (first: &'storage cell 'left u32, left: &'left mut Permit, second: &'storage cell 'right u32, right: &'right mut Permit) {} fn f 'storage 'permission (first: &'storage cell 'permission u32, second: &'storage cell 'permission u32, permission: &'permission mut Permit) { separate 'storage 'permission 'permission(first, permission, second, permission); }"); |
| 1893 | let error = try super::expectError(&result); |
| 1894 | let case resolver::ErrorKind::RegionInference(_) = error.kind |
| 1895 | else throw testing::TestError::Failed; |
| 1896 | } |
| 1897 | } |
| 1898 | |
| 1899 | /// Applied nominal fields preserve distinct formal permission identities at calls. |
| 1900 | @test unsafe fn testPermissionCellNominalGenericArguments() throws (testing::TestError) { |
| 1901 | { |
| 1902 | let mut arena = super::testArena(); |
| 1903 | let storage: 'test = &mut arena in { |
| 1904 | let mut res = super::testResolver(storage); |
| 1905 | let result = try super::resolveProgramStr(&mut res, |
| 1906 | "record Permit {} record Wrapper: 'storage + 'permission + Copy { cell: &'storage cell 'permission u32 } fn separate 'storage 'left 'right (first: Wrapper 'storage 'left, left: &'left mut Permit, second: Wrapper 'storage 'right, right: &'right mut Permit) {} fn f 'storage 'left 'right (first: Wrapper 'storage 'left, left: &'left mut Permit, second: Wrapper 'storage 'right, right: &'right mut Permit) { separate(first, left, second, right); }"); |
| 1907 | try super::expectNoErrors(&result); |
| 1908 | } |
| 1909 | } |
| 1910 | { |
| 1911 | let mut arena = super::testArena(); |
| 1912 | let storage: 'test = &mut arena in { |
| 1913 | let mut res = super::testResolver(storage); |
| 1914 | let result = try super::resolveProgramStr(&mut res, |
| 1915 | "record Permit {} record Wrapper: 'storage + 'permission + Copy { cell: &'storage cell 'permission u32 } fn separate 'storage 'left 'right (first: Wrapper 'storage 'left, left: &'left mut Permit, second: Wrapper 'storage 'right, right: &'right mut Permit) {} fn f 'storage 'permission (first: Wrapper 'storage 'permission, second: Wrapper 'storage 'permission, permission: &'permission mut Permit) { separate(first, permission, second, permission); }"); |
| 1916 | let error = try super::expectError(&result); |
| 1917 | let case resolver::ErrorKind::RegionInference(_) = error.kind |
| 1918 | else throw testing::TestError::Failed; |
| 1919 | } |
| 1920 | } |
| 1921 | } |
| 1922 | |
| 1923 | /// Direct and callback cell accesses cannot conflict with a live payload loan. |
| 1924 | @test unsafe fn testPermissionCellCallConflicts() throws (testing::TestError) { |
| 1925 | for program in [ |
| 1926 | // Direct call. |
| 1927 | "record Permit {} fn mutate 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { set *cell = 1; } fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let authority: 'loan = &*permission, payload = &*cell in { mutate(cell, permission); let value = *payload; } }", |
| 1928 | // Region-instantiated callback. |
| 1929 | "record Permit {} fn mutate 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { set *cell = 1; } fn f 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let callback = mutate 'storage 'permission; let authority: 'loan = &*permission, payload = &*cell in { callback(cell, permission); let value = *payload; } }", |
| 1930 | ] { |
| 1931 | let mut arena = super::testArena(); |
| 1932 | let storage: 'test = &mut arena in { |
| 1933 | let mut res = super::testResolver(storage); |
| 1934 | let result = try super::resolveProgramStr(&mut res, program); |
| 1935 | let error = try super::expectError(&result); |
| 1936 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind |
| 1937 | else throw testing::TestError::Failed; |
| 1938 | } |
| 1939 | } |
| 1940 | } |
| 1941 | |
| 1942 | /// Payload references cannot escape their matching permission loan. |
| 1943 | @test unsafe fn testPermissionCellLoanEscape() throws (testing::TestError) { |
| 1944 | let mut arena = super::testArena(); |
| 1945 | let storage: 'test = &mut arena in { |
| 1946 | let mut res = super::testResolver(storage); |
| 1947 | let result = try super::resolveProgramStr(&mut res, |
| 1948 | "record Permit {} fn escape 'storage 'permission (cell: &'storage cell 'permission u32, permission: &'permission mut Permit) -> &'storage u32 { let authority: 'loan = &*permission, payload = &*cell in { return payload; } }"); |
| 1949 | let error = try super::expectError(&result); |
| 1950 | let case resolver::ErrorKind::RegionEscape(_) = error.kind |
| 1951 | else throw testing::TestError::Failed; |
| 1952 | } |
| 1953 | } |
| 1954 | |
| 1955 | /// Allocation-session escape and reset remain invalid while an associated payload reference is live. |
| 1956 | @test unsafe fn testPermissionCellStorageLiveness() throws (testing::TestError) { |
| 1957 | { |
| 1958 | let mut arena = super::testArena(); |
| 1959 | let storage: 'test = &mut arena in { |
| 1960 | let mut res = super::testResolver(storage); |
| 1961 | // A payload reference cannot escape the allocation session that owns its cell storage. |
| 1962 | let result = try super::resolveSessionProgramStr(&mut res, |
| 1963 | "use std::lang::alloc; record Permit {} fn escape 'arena (arena: &'arena mut alloc::Arena) -> &'arena u32 { let mut owner = Permit {}; let permission: 'permission = &mut owner in { use *arena as session in { let place = try! session.new(1 as u32); let cell = &cell 'permission *place; let authority: 'loan = &*permission, payload = &*cell in { return payload; } } } }"); |
| 1964 | let error = try super::expectError(&result); |
| 1965 | let case resolver::ErrorKind::RegionEscape(_) = error.kind |
| 1966 | else throw testing::TestError::Failed; |
| 1967 | } |
| 1968 | } |
| 1969 | { |
| 1970 | let mut arena = super::testArena(); |
| 1971 | let storage: 'test = &mut arena in { |
| 1972 | let mut res = super::testResolver(storage); |
| 1973 | // Reset cannot reclaim a cell's storage while its payload reference is live. |
| 1974 | let result = try super::resolveSessionProgramStr(&mut res, |
| 1975 | "use std::lang::alloc; record Permit {} fn reset 'storage 'permission (arena: &'storage mut alloc::Arena, cell: &'storage cell 'permission u32, permission: &'permission mut Permit) { let authority: 'loan = &*permission, payload = &*cell in { alloc::reset(arena); let value = *payload; } }"); |
| 1976 | let error = try super::expectError(&result); |
| 1977 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind |
| 1978 | else throw testing::TestError::Failed; |
| 1979 | } |
| 1980 | } |
| 1981 | } |
| 1982 | |
| 1983 | /// Permission-associated cells admit affine values that are safe for bulk reclamation. |
| 1984 | @test unsafe fn testPermissionCellAffinePayload() throws (testing::TestError) { |
| 1985 | for program in [ |
| 1986 | // Construction accepts an affine, bulk-discardable record. |
| 1987 | "record Permit {} record Payload { value: u32 } fn f 'permission (place: &mut Payload, permission: &'permission mut Permit) { let cell: &cell 'permission Payload = &cell 'permission *place; }", |
| 1988 | // Matching shared authority permits an ordinary shared payload loan. |
| 1989 | "record Permit {} record Payload { value: u32 } fn observe(value: &Payload) {} fn f 'storage 'permission (cell: &'storage cell 'permission Payload, permission: &'permission mut Permit) { let authority: 'loan = &*permission, payload = &*cell in { observe(payload); } }", |
| 1990 | // Matching mutable authority permits an ordinary mutable helper call. |
| 1991 | "record Permit {} record Payload { value: u32 } fn increment(value: &mut Payload) { set value.value += 1; } fn f 'storage 'permission (cell: &'storage cell 'permission Payload, permission: &'permission mut Permit) { let authority: 'loan = &mut *permission, payload = &mut *cell in { increment(payload); } }", |
| 1992 | // Replacement evaluates the new value from the old payload before overwriting it. |
| 1993 | "record Permit {} record Payload { value: u32 } fn replacement(value: &Payload) -> Payload { return Payload { value: value.value + 1 }; } fn f 'storage 'permission (cell: &'storage cell 'permission Payload, permission: &'permission mut Permit) { set *cell = replacement(&*cell); }", |
| 1994 | ] { |
| 1995 | let mut arena = super::testArena(); |
| 1996 | let storage: 'test = &mut arena in { |
| 1997 | let mut res = super::testResolver(storage); |
| 1998 | let result = try super::resolveProgramStr(&mut res, program); |
| 1999 | try super::expectNoErrors(&result); |
| 2000 | } |
| 2001 | } |
| 2002 | } |
| 2003 | |
| 2004 | /// Cell payload eligibility remains narrow without a bulk-discardable permission association. |
| 2005 | @test unsafe fn testPermissionCellAffinePayloadEligibility() throws (testing::TestError) { |
| 2006 | for program in [ |
| 2007 | // An unassociated cell still requires a Copy payload. |
| 2008 | "record Payload { value: u32 } fn f(place: &mut Payload) { let cell = &cell *place; }", |
| 2009 | // A linear payload cannot be discarded by replacement or arena reclamation. |
| 2010 | "record Permit {} record Payload: Once { value: u32 } fn f 'permission (place: &mut Payload, permission: &'permission mut Permit) { let cell: &cell 'permission Payload = &cell 'permission *place; }", |
| 2011 | ] { |
| 2012 | let mut arena = super::testArena(); |
| 2013 | let storage: 'test = &mut arena in { |
| 2014 | let mut res = super::testResolver(storage); |
| 2015 | let result = try super::resolveProgramStr(&mut res, program); |
| 2016 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidCellPayload); |
| 2017 | } |
| 2018 | } |
| 2019 | } |
| 2020 | |
| 2021 | /// Affine associated payloads can only be accessed through explicit borrows or replacement. |
| 2022 | @test unsafe fn testPermissionCellAffinePayloadMoves() throws (testing::TestError) { |
| 2023 | for program in [ |
| 2024 | // A value-context dereference cannot move the whole payload out of shared storage. |
| 2025 | "record Permit {} record Payload { value: u32 } fn f 'storage 'permission (cell: &'storage cell 'permission Payload, permission: &'permission mut Permit) { let moved = *cell; }", |
| 2026 | // A shared payload loan cannot move an affine field out of the stored value. |
| 2027 | "record Permit {} record Item { value: u32 } record Payload { item: Item } fn f 'storage 'permission (cell: &'storage cell 'permission Payload, permission: &'permission mut Permit) { let authority: 'loan = &*permission, payload = &*cell in { let moved = payload.item; } }", |
| 2028 | ] { |
| 2029 | let mut arena = super::testArena(); |
| 2030 | let storage: 'test = &mut arena in { |
| 2031 | let mut res = super::testResolver(storage); |
| 2032 | let result = try super::resolveProgramStr(&mut res, program); |
| 2033 | try super::expectErrorKind(&result, resolver::ErrorKind::LinearPartialMove); |
| 2034 | } |
| 2035 | } |
| 2036 | } |
| 2037 | |
| 2038 | /// Observing an affine payload value still requires an explicit shared reference. |
| 2039 | @test unsafe fn testPermissionCellAffinePayloadObserve() throws (testing::TestError) { |
| 2040 | { |
| 2041 | let mut arena = super::testArena(); |
| 2042 | let storage: 'test = &mut arena in { |
| 2043 | let mut res = super::testResolver(storage); |
| 2044 | let result = try super::resolveProgramStr(&mut res, |
| 2045 | "record Permit {} record Payload { value: u32 } fn f 'storage 'permission (cell: &'storage cell 'permission Payload, permission: &'permission mut Permit) { let equal = *cell == *cell; }"); |
| 2046 | try super::expectErrorKind(&result, resolver::ErrorKind::LinearPartialMove); |
| 2047 | } |
| 2048 | } |
| 2049 | { |
| 2050 | let mut arena = super::testArena(); |
| 2051 | let storage: 'test = &mut arena in { |
| 2052 | let mut res = super::testResolver(storage); |
| 2053 | let result = try super::resolveProgramStr(&mut res, |
| 2054 | "record Permit {} record Payload { value: u32 } fn f 'storage 'permission (cell: &'storage cell 'permission Payload, permission: &'permission mut Permit) { let authority: 'loan = &*permission, left = &*cell, right = &*cell in { let sameReference = left == right; let sameValue = left.value == right.value; } }"); |
| 2055 | try super::expectNoErrors(&result); |
| 2056 | } |
| 2057 | } |
| 2058 | } |
| 2059 | |
| 2060 | /// Cell creation requires exclusive access for the full borrow duration. |
| 2061 | @test unsafe fn testCellBorrowConflicts() throws (testing::TestError) { |
| 2062 | for program in [ |
| 2063 | "fn f() { let mut n: u32 = 0; let c = &cell n; let r = &n; }", |
| 2064 | "unsafe fn f() { let mut n: u32 = 0; let c = &cell n; let r = &n; }", |
| 2065 | "fn f() { let mut n: u32 = 0; let r = &n; let c = &cell n; }", |
| 2066 | "fn f() { let mut n: u32 = 0; let c = &cell n; set n = 1; }", |
| 2067 | "fn f() { let mut n: u32 = 0; let c = &cell n; let other = &cell n; }", |
| 2068 | "fn f() { let mut n: u32 = 0; let c = &cell n as &cell u32; let r = &n; }", |
| 2069 | "fn inspect(c: &cell u32, r: &u32) {} fn f() { let mut n: u32 = 0; inspect(&cell n, &n); }", |
| 2070 | "fn inspect(r: &u32, c: &cell u32) {} fn f() { let mut n: u32 = 0; inspect(&n, &cell n); }", |
| 2071 | ] { |
| 2072 | let mut arena = super::testArena(); |
| 2073 | let storage: 'test = &mut arena in { |
| 2074 | let mut res = super::testResolver(storage); |
| 2075 | let result = try super::resolveProgramStr(&mut res, program); |
| 2076 | let error = try super::expectError(&result); |
| 2077 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 2078 | } |
| 2079 | } |
| 2080 | } |
| 2081 | |
| 2082 | /// Cell creation requires an exclusive initialized place. |
| 2083 | @test unsafe fn testCellBorrowPayload() throws (testing::TestError) { |
| 2084 | for program in [ |
| 2085 | "fn f() { let n: u32 = 0; let c = &cell n; }", |
| 2086 | "fn f(p: &u32) { let c = &cell *p; }", |
| 2087 | "unsafe fn f(p: &u32) { let c = &cell *p; }", |
| 2088 | ] { |
| 2089 | let mut arena = super::testArena(); |
| 2090 | let storage: 'test = &mut arena in { |
| 2091 | let mut res = super::testResolver(storage); |
| 2092 | let result = try super::resolveProgramStr(&mut res, program); |
| 2093 | try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding); |
| 2094 | } |
| 2095 | } |
| 2096 | for program in [ |
| 2097 | "record R { n: u32 } fn f() { let mut r = R { n: 0 }; let c = &cell r; }", |
| 2098 | "fn f(p: &mut *mut u32) { let c = &cell *p; }", |
| 2099 | "fn f(p: &mut [u32]) { let c = &cell p[..]; }", |
| 2100 | ] { |
| 2101 | let mut arena = super::testArena(); |
| 2102 | let storage: 'test = &mut arena in { |
| 2103 | let mut res = super::testResolver(storage); |
| 2104 | let result = try super::resolveProgramStr(&mut res, program); |
| 2105 | try super::expectErrorKind(&result, resolver::ErrorKind::InvalidCellPayload); |
| 2106 | } |
| 2107 | } |
| 2108 | } |
| 2109 | |
| 2110 | /// Cell access cannot expose payload references or restore exclusive pointers. |
| 2111 | @test unsafe fn testCellPointerAccess() throws (testing::TestError) { |
| 2112 | for program in [ |
| 2113 | "fn f(p: *cell u32) { let r = &*p; }", |
| 2114 | "unsafe fn f(p: *cell u32) { let r = &*p; }", |
| 2115 | "record R: Copy { n: u32 } fn f(p: *cell R) { let r = &(*p).n; }", |
| 2116 | "fn f(p: *cell [u32; 2]) { let r = &(*p)[0]; }", |
| 2117 | ] { |
| 2118 | let mut testArena57 = super::testArena(); |
| 2119 | let testStorage57: 'test57 = &mut testArena57 in { |
| 2120 | let mut res = super::testResolver(testStorage57); |
| 2121 | let result = try super::resolveProgramStr(&mut res, program); |
| 2122 | try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding); |
| 2123 | } |
| 2124 | } |
| 2125 | for program in [ |
| 2126 | "fn f(p: *cell u32) -> *mut u32 { return p as *mut u32; }", |
| 2127 | "unsafe fn f(p: *cell u32) -> *unsafe mut u32 { return p as *unsafe mut u32; }", |
| 2128 | "fn f(p: *u32) -> *cell u32 { return p as *cell u32; }", |
| 2129 | "unsafe fn f(p: *unsafe mut u32) -> *cell u32 { return p as *cell u32; }", |
| 2130 | "fn f(p: *mut u64) -> *cell u32 { return p as *cell u32; }", |
| 2131 | "fn f 'r (p: &'r mut u32) -> *cell u32 { return p as *cell u32; }", |
| 2132 | ] { |
| 2133 | let mut testArena58 = super::testArena(); |
| 2134 | let testStorage58: 'test58 = &mut testArena58 in { |
| 2135 | let mut res = super::testResolver(testStorage58); |
| 2136 | let result = try super::resolveProgramStr(&mut res, program); |
| 2137 | let error = try super::expectError(&result); |
| 2138 | let case resolver::ErrorKind::InvalidAsCast(_) = error.kind else throw testing::TestError::Failed; |
| 2139 | } |
| 2140 | } |
| 2141 | } |
| 2142 | |
| 2143 | /// An exclusive payload borrow cannot be made from shared cell access. |
| 2144 | @test unsafe fn testCellPointerMutablePayload() throws (testing::TestError) { |
| 2145 | let mut testArena59 = super::testArena(); |
| 2146 | let testStorage59: 'test59 = &mut testArena59 in { |
| 2147 | let mut res = super::testResolver(testStorage59); |
| 2148 | let result = try super::resolveProgramStr(&mut res, |
| 2149 | "fn f(p: *cell u32) { let r = &mut *p; }"); |
| 2150 | try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding); |
| 2151 | } |
| 2152 | } |
| 2153 | |
| 2154 | /// Cell conversion consumes exclusive access, including call arguments. |
| 2155 | @test unsafe fn testCellPointerOwnership() throws (testing::TestError) { |
| 2156 | for program in [ |
| 2157 | "fn f(p: *mut u32) { let c = &cell *p; set *p = 1; }", |
| 2158 | "unsafe fn f(p: *mut u32) { let c = &cell *p; set *p = 1; }", |
| 2159 | "record R: Copy { n: u32 } fn f(p: *mut R) { let c = &cell p.n; set p.n = 1; }", |
| 2160 | "fn f(p: *mut [u32]) { let c = &cell p[0]; set p[0] = 1; }", |
| 2161 | "fn f(p: *mut u32) { let c = p as *cell u32; set *p = 1; }", |
| 2162 | "unsafe fn f(p: *mut u32) { let c = p as *cell u32; set *p = 1; }", |
| 2163 | "fn read(p: &cell u32) {} fn f(p: &mut u32) { read(p as &cell u32); set *p = 1; }", |
| 2164 | ] { |
| 2165 | let mut testArena60 = super::testArena(); |
| 2166 | let testStorage60: 'test60 = &mut testArena60 in { |
| 2167 | let mut res = super::testResolver(testStorage60); |
| 2168 | let result = try super::resolveProgramStr(&mut res, program); |
| 2169 | let error = try super::expectError(&result); |
| 2170 | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
| 2171 | } |
| 2172 | } |
| 2173 | for program in [ |
| 2174 | "fn f() { let mut n: u32 = 0; let c = &mut n as &cell u32; let r = &n; set *c = 1; }", |
| 2175 | "unsafe fn f() { let mut n: u32 = 0; let c = &mut n as &cell u32; let r = &n; set *c = 1; }", |
| 2176 | ] { |
| 2177 | let mut testArena61 = super::testArena(); |
| 2178 | let testStorage61: 'test61 = &mut testArena61 in { |
| 2179 | let mut res = super::testResolver(testStorage61); |
| 2180 | let result = try super::resolveProgramStr(&mut res, program); |
| 2181 | let error = try super::expectError(&result); |
| 2182 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 2183 | } |
| 2184 | } |
| 2185 | } |
| 2186 | |
| 2187 | /// Cell writes preserve exact payload regions and destination lifetimes. |
| 2188 | @test unsafe fn testCellPointerRegion() throws (testing::TestError) { |
| 2189 | for program in [ |
| 2190 | "fn f 'r 's (p: &'r cell &'r u32, value: &'s u32) { set *p = value; }", |
| 2191 | "unsafe fn f 'r 's (p: &'r cell &'r u32, value: &'s u32) { set *p = value; }", |
| 2192 | "fn f 'r 's (p: &'r cell u32) -> &'s cell u32 { return p; }", |
| 2193 | ] { |
| 2194 | let mut testArena62 = super::testArena(); |
| 2195 | let testStorage62: 'test62 = &mut testArena62 in { |
| 2196 | let mut res = super::testResolver(testStorage62); |
| 2197 | let result = try super::resolveProgramStr(&mut res, program); |
| 2198 | let error = try super::expectError(&result); |
| 2199 | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
| 2200 | } |
| 2201 | } |
| 2202 | for program in [ |
| 2203 | "fn f 'r 's (p: &'r cell &'s u32, value: &'s u32) where 'r: 's { set *p = value; }", |
| 2204 | "unsafe fn f 'r 's (p: &'r cell &'s u32, value: &'s u32) where 'r: 's { set *p = value; }", |
| 2205 | ] { |
| 2206 | let mut testArena63 = super::testArena(); |
| 2207 | let testStorage63: 'test63 = &mut testArena63 in { |
| 2208 | let mut res = super::testResolver(testStorage63); |
| 2209 | let result = try super::resolveProgramStr(&mut res, program); |
| 2210 | let error = try super::expectError(&result); |
| 2211 | let case resolver::ErrorKind::RegionEscape(_) = error.kind else throw testing::TestError::Failed; |
| 2212 | } |
| 2213 | } |
| 2214 | } |
| 2215 | |
| 2216 | /// Raw cell operations require an unsafe execution context. |
| 2217 | @test unsafe fn testCellPointerRawAccess() throws (testing::TestError) { |
| 2218 | for program in [ |
| 2219 | "fn f(p: *unsafe cell u32) -> u32 { return *p; }", |
| 2220 | "fn f(p: *unsafe cell u32) { set *p = 1; }", |
| 2221 | "fn f(p: &*unsafe cell u32) { set **p = 1; }", |
| 2222 | ] { |
| 2223 | let mut testArena64 = super::testArena(); |
| 2224 | let testStorage64: 'test64 = &mut testArena64 in { |
| 2225 | let mut res = super::testResolver(testStorage64); |
| 2226 | let result = try super::resolveProgramStr(&mut res, program); |
| 2227 | try super::expectErrorKind(&result, resolver::ErrorKind::UnsafeOperation); |
| 2228 | } |
| 2229 | } |
| 2230 | } |
| 2231 | |
| 2232 | /// A checked buffer context cannot be copied while its exclusive storage is live. |
| 2233 | @test unsafe fn testCheckedBufferOwnership() throws (testing::TestError) { |
| 2234 | for program in [ |
| 2235 | "record Buffer: 'r { words: &'r mut [u32] } fn observe 'r (b: &Buffer 'r) {} fn f 'r (b: Buffer 'r) { let moved = b; observe(&b); }", |
| 2236 | "record Buffer: 'r { words: &'r mut [u32] } unsafe fn observe 'r (b: &Buffer 'r) {} unsafe fn f 'r (b: Buffer 'r) { let moved = b; observe(&b); }", |
| 2237 | ] { |
| 2238 | let mut testArena65 = super::testArena(); |
| 2239 | let testStorage65: 'test65 = &mut testArena65 in { |
| 2240 | let mut res = super::testResolver(testStorage65); |
| 2241 | let result = try super::resolveProgramStr(&mut res, program); |
| 2242 | let error = try super::expectError(&result); |
| 2243 | let case resolver::ErrorKind::AffineUseAfterMove(_) = error.kind else throw testing::TestError::Failed; |
| 2244 | } |
| 2245 | } |
| 2246 | } |
| 2247 | |
| 2248 | /// An iterator's stored shared borrow excludes writes through the buffer owner. |
| 2249 | @test unsafe fn testCheckedIteratorLoan() throws (testing::TestError) { |
| 2250 | for program in [ |
| 2251 | "record Buffer: 'r { words: &'r mut [u32] } record Iter: 'r + 'v + Copy where 'r: 'v { source: &'v Buffer 'r } fn f 'r (b: Buffer 'r) { let mut owner = b; let view: 'scan = &owner where 'r: 'scan in { let iterator: Iter 'r 'scan = Iter { source: view }; set owner.words[0] = 1; } }", |
| 2252 | "record Buffer: 'r { words: &'r mut [u32] } record Iter: 'r + 'v + Copy where 'r: 'v { source: &'v Buffer 'r } unsafe fn f 'r (b: Buffer 'r) { let mut owner = b; let view: 'scan = &owner where 'r: 'scan in { let iterator: Iter 'r 'scan = Iter { source: view }; set owner.words[0] = 1; } }", |
| 2253 | ] { |
| 2254 | let mut testArena66 = super::testArena(); |
| 2255 | let testStorage66: 'test66 = &mut testArena66 in { |
| 2256 | let mut res = super::testResolver(testStorage66); |
| 2257 | let result = try super::resolveProgramStr(&mut res, program); |
| 2258 | let error = try super::expectError(&result); |
| 2259 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind else throw testing::TestError::Failed; |
| 2260 | } |
| 2261 | } |
| 2262 | } |
| 2263 | |
| 2264 | /// An inferred raw pointer argument requires an unsafe address operation. |
| 2265 | @test unsafe fn testRegionCallRawAddress() throws (testing::TestError) { |
| 2266 | let mut arena = super::testArena(); |
| 2267 | let storage: 'arena = &mut arena in { |
| 2268 | let mut res = super::testResolver(storage); |
| 2269 | let result = try super::resolveProgramStr(&mut res, |
| 2270 | "record View: 'r + Copy { value: &'r u32 } fn read 'r (view: *unsafe View 'r) {} fn f 'r (view: View 'r) { read(&view); }"); |
| 2271 | try super::expectErrorKind(&result, resolver::ErrorKind::UnsafeOperation); |
| 2272 | } |
| 2273 | } |
| 2274 | |
| 2275 | /// Shared aggregate access cannot expose mutable access to an exclusive field. |
| 2276 | @test unsafe fn testSharedExclusiveFieldAccess() throws (testing::TestError) { |
| 2277 | for program in [ |
| 2278 | "record H: 'r { items: &'r mut [u32] } fn f 'r (h: &H 'r) { set h.items[0] = 42; }", |
| 2279 | "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r (h: &H 'r) { set h.items[0] = 42; }", |
| 2280 | "record H: 'r { items: &'r mut [u32] } fn f 'r (h: &'r H 'r) { let p = &mut h.items[0]; }", |
| 2281 | "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r (h: &'r H 'r) { let p = &mut h.items[0]; }", |
| 2282 | "record H: 'r { items: &'r mut [u32] } fn write(p: &mut [u32]) {} fn f 'r (h: &H 'r) { write(h.items); }", |
| 2283 | "record H: 'r { items: &'r mut [u32] } fn write(p: &mut [u32]) {} unsafe fn f 'r (h: &H 'r) { write(h.items); }", |
| 2284 | "record H: 'r { item: &'r mut u32 } fn f 'r (h: &H 'r) { let p: &mut u32 = h.item; }", |
| 2285 | "record H: 'r { item: &'r mut u32 } unsafe fn f 'r (h: &H 'r) { let p: &mut u32 = h.item; }", |
| 2286 | "record H { item: *mut u32 } fn f(h: &H) { set *h.item = 42; }", |
| 2287 | "record H { item: *mut u32 } unsafe fn f(h: &H) { set *h.item = 42; }", |
| 2288 | "record H: 'r { items: [&'r mut u32; 1] } fn f 'r (h: &H 'r) { set *h.items[0] = 42; }", |
| 2289 | "record H: 'r { items: [&'r mut u32; 1] } unsafe fn f 'r (h: &H 'r) { set *h.items[0] = 42; }", |
| 2290 | "fn f 'r (items: &'r [&'r mut u32]) { set *items[0] = 42; }", |
| 2291 | "unsafe fn f 'r (items: &'r [&'r mut u32]) { set *items[0] = 42; }", |
| 2292 | "fn f 'r (p: &'r &'r mut u32) { set **p = 42; }", |
| 2293 | "unsafe fn f 'r (p: &'r &'r mut u32) { set **p = 42; }", |
| 2294 | "record H: 'r { item: &'r mut u32 } fn write(p: &mut u32) {} fn f 'r (h: &H 'r, flag: bool) { write(h.item if flag else h.item); }", |
| 2295 | "record H: 'r { item: &'r mut u32 } fn write(p: &mut u32) {} unsafe fn f 'r (h: &H 'r, flag: bool) { write(h.item if flag else h.item); }", |
| 2296 | "record V { n: u32 } record H { item: *mut V } fn (v: &mut V) write() { set v.n = 42; } fn f(h: &H) { h.item.write(); }", |
| 2297 | "record V { n: u32 } record H { item: *mut V } fn (v: &mut V) write() { set v.n = 42; } unsafe fn f(h: &H) { h.item.write(); }", |
| 2298 | "trait W { fn (&mut W) write(); } record H: 'r { object: &'r mut opaque W } fn f 'r (h: &H 'r) { h.object.write(); }", |
| 2299 | "trait W { fn (&mut W) write(); } record H: 'r { object: &'r mut opaque W } unsafe fn f 'r (h: &H 'r) { h.object.write(); }", |
| 2300 | ] { |
| 2301 | let mut arena = super::testArena(); |
| 2302 | let storage: 'test = &mut arena in { |
| 2303 | let mut res = super::testResolver(storage); |
| 2304 | let result = try super::resolveProgramStr(&mut res, program); |
| 2305 | try super::expectErrorKind(&result, resolver::ErrorKind::ImmutableBinding); |
| 2306 | } |
| 2307 | } |
| 2308 | } |
| 2309 | |
| 2310 | /// Named shared pattern bindings retain the subject reference region. |
| 2311 | @test unsafe fn testNamedPatternBorrowRegions() throws (testing::TestError) { |
| 2312 | for program in [ |
| 2313 | "record B: 'r { values: &'r mut [u32] } fn f 'r (b: &'r B 'r) -> &'r [u32] { return &b.values[..]; }", |
| 2314 | "record B: 'r { values: &'r mut [u32] } union U: 'r { Some(B 'r), None } fn f 'r (u: &'r U 'r) -> &'r [u32] { match u { case U::Some(b) => return &b.values[..], else => panic, } }", |
| 2315 | "record B: 'r { values: &'r mut [u32] } record S: 'r { items: [?B 'r; 1] } fn f 'r (s: &'r S 'r) -> &'r [u32] { match &s.items[0] { case nil => panic, b => return &b.values[..], } }", |
| 2316 | "record B: 'r { values: &'r mut [u32] } union U: 'r { Some(B 'r), None } fn values 'r (b: &'r B 'r) -> &'r [u32] { return &b.values[..]; } fn f 'r (u: &'r U 'r) -> &'r [u32] { match u { case U::Some(b) => return values(b), else => panic, } }", |
| 2317 | "record B: 'r { values: &'r mut [u32] } record S: 'r { items: [?B 'r; 1] } fn values 'r (b: &'r B 'r) -> &'r [u32] { return &b.values[..]; } fn f 'r (s: &'r S 'r) -> &'r [u32] { let mut found: ?&'r B 'r = nil; match &s.items[0] { case nil => panic, b => set found = b, } let b = found else panic; return values(b); }", |
| 2318 | "record B: 'r { values: &'r mut [u32] } union U: 'r { Some(B 'r), None } fn f 'r (u: &'r U 'r) -> &'r [u32] { if let case U::Some(b) = u { return &b.values[..]; } panic; }", |
| 2319 | "record B: 'r { values: &'r mut [u32] } union U: 'r { Some(B 'r), None } fn f 'r (u: &'r U 'r) -> &'r [u32] { let case U::Some(b) = u else panic; return &b.values[..]; }", |
| 2320 | "record B: 'r { values: &'r mut [u32] } union U: 'r { Some(B 'r), None } fn f 'r (u: &'r U 'r) -> &'r [u32] { while let case U::Some(b) = u { return &b.values[..]; } panic; }", |
| 2321 | ] { |
| 2322 | let mut arena = super::testArena(); |
| 2323 | let storage: 'test = &mut arena in { |
| 2324 | let mut res = super::testResolver(storage); |
| 2325 | let result = try super::resolveProgramStr(&mut res, program); |
| 2326 | try super::expectNoErrors(&result); |
| 2327 | } |
| 2328 | } |
| 2329 | } |
| 2330 | |
| 2331 | /// Owned and raw pattern subjects produce ordinary shared bindings. |
| 2332 | @test unsafe fn testPointerPatternBindingClasses() throws (testing::TestError) { |
| 2333 | for program in [ |
| 2334 | "record B { value: u32 } union U { Some(B), None } fn f(u: *U) { match u { case U::Some(b) => { assert b.value == 0; }, else => {}, } }", |
| 2335 | "record B { value: u32 } union U { Some(B), None } unsafe fn f(u: *unsafe U) { match u { case U::Some(b) => { assert b.value == 0; }, else => {}, } }", |
| 2336 | ] { |
| 2337 | let mut arena = super::testArena(); |
| 2338 | let storage: 'test = &mut arena in { |
| 2339 | let mut res = super::testResolver(storage); |
| 2340 | let result = try super::resolveProgramStr(&mut res, program); |
| 2341 | try super::expectNoErrors(&result); |
| 2342 | let func = try super::getBlockStmt(result.root, 2); |
| 2343 | let fnType = resolver::typeFor(&res, func) else throw testing::TestError::Failed; |
| 2344 | let case resolver::Type::Fn(info) = fnType else throw testing::TestError::Failed; |
| 2345 | let subject = resolver::unwrapMatchSubject(*info.paramTypes[0]); |
| 2346 | let case resolver::MatchBy::Ref(types::PointerClass::Ref) = subject.by |
| 2347 | else throw testing::TestError::Failed; |
| 2348 | let case ast::NodeValue::FnDecl(decl) = func.value else throw testing::TestError::Failed; |
| 2349 | let body = decl.body else throw testing::TestError::Failed; |
| 2350 | let statement = try super::getBlockStmt(body, 0); |
| 2351 | let case ast::NodeValue::Match(matchStmt) = statement.value else throw testing::TestError::Failed; |
| 2352 | let case ast::NodeValue::MatchProng(prong) = matchStmt.prongs[0].value |
| 2353 | else throw testing::TestError::Failed; |
| 2354 | let case ast::ProngArm::Case(patterns) = prong.arm else throw testing::TestError::Failed; |
| 2355 | let case ast::NodeValue::Call(call) = patterns[0].value else throw testing::TestError::Failed; |
| 2356 | let bindingType = resolver::typeFor(&res, call.args[0]) else throw testing::TestError::Failed; |
| 2357 | let case resolver::Type::Pointer { class: types::PointerClass::Ref, .. } = bindingType |
| 2358 | else throw testing::TestError::Failed; |
| 2359 | } |
| 2360 | } |
| 2361 | } |
| 2362 | |
| 2363 | /// Mutable pattern payload bindings cannot escape or overlap source mutation. |
| 2364 | @test unsafe fn testMutablePatternBindingEscape() throws (testing::TestError) { |
| 2365 | let programs = [ |
| 2366 | "record B: 'r { values: &'r mut [u32] } union U: 'r { Some(B 'r), None } fn f 'r (u: &'r mut U 'r) -> &'r [u32] { match u { case U::Some(b) => return &b.values[..], else => panic, } }", |
| 2367 | ]; |
| 2368 | for program in programs { |
| 2369 | let mut arena = super::testArena(); |
| 2370 | let storage: 'test = &mut arena in { |
| 2371 | let mut res = super::testResolver(storage); |
| 2372 | let result = try super::resolveProgramStr(&mut res, program); |
| 2373 | let error = try super::expectError(&result); |
| 2374 | let case resolver::ErrorKind::TypeMismatch(_) = error.kind |
| 2375 | else throw testing::TestError::Failed; |
| 2376 | } |
| 2377 | } |
| 2378 | for program in [ |
| 2379 | "record B: 'r { values: &'r mut [u32] } union U: 'r { Some(B 'r), None } fn f 'r (u: &'r mut U 'r) { match u { case U::Some(b) => { let held = &b.values[..]; set *u = U::None; assert held.len == 0; }, else => {}, } }", |
| 2380 | "record B: 'r { values: &'r mut [u32] } union U: 'r { Some(B 'r), None } fn f 'r (u: &'r mut U 'r) { let mut held: ?&'r B 'r = nil; match &*u { case U::Some(b) => set held = b, else => {}, } set *u = U::None; let b = held else return; assert b.values.len == 0; }", |
| 2381 | ] { |
| 2382 | let mut arena = super::testArena(); |
| 2383 | let storage: 'test = &mut arena in { |
| 2384 | let mut res = super::testResolver(storage); |
| 2385 | let result = try super::resolveProgramStr(&mut res, program); |
| 2386 | let error = try super::expectError(&result); |
| 2387 | let case resolver::ErrorKind::BorrowConflict(_) = error.kind |
| 2388 | else throw testing::TestError::Failed; |
| 2389 | } |
| 2390 | } |
| 2391 | } |
| 2392 | |
| 2393 | /// Named shared pattern bindings cannot outlive a shorter subject reference. |
| 2394 | @test unsafe fn testNamedPatternBorrowRejectsShortOwner() throws (testing::TestError) { |
| 2395 | for program in [ |
| 2396 | "record B: 'r { values: &'r mut [u32] } union U: 'r { Some(B 'r), None } fn f 'r 's (u: &'s U 'r) -> &'r [u32] where 'r: 's { match u { case U::Some(b) => return &b.values[..], else => panic, } }", |
| 2397 | "record B: 'r { values: &'r mut [u32] } record S: 'r { items: [?B 'r; 1] } fn f 'r 's (s: &'s S 'r) -> &'r [u32] where 'r: 's { match &s.items[0] { case nil => panic, b => return &b.values[..], } }", |
| 2398 | ] { |
| 2399 | let mut arena = super::testArena(); |
| 2400 | let storage: 'test = &mut arena in { |
| 2401 | let mut res = super::testResolver(storage); |
| 2402 | let result = try super::resolveProgramStr(&mut res, program); |
| 2403 | let error = try super::expectError(&result); |
| 2404 | let case resolver::ErrorKind::TypeMismatch(_) = error.kind |
| 2405 | else throw testing::TestError::Failed; |
| 2406 | } |
| 2407 | } |
| 2408 | } |
| 2409 | |
| 2410 | /// A reference through an exclusive field cannot outlive the owner's borrow. |
| 2411 | @test unsafe fn testExclusiveOwnerBorrowLifetimes() throws (testing::TestError) { |
| 2412 | for program in [ |
| 2413 | "record H: 'r { item: &'r mut u32 } fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { let p: &'r u32 = h.item; return p; }", |
| 2414 | "record H: 'r { item: ?&'r mut u32 } fn f 'r 's (h: &'s H 'r) -> ?&'r u32 where 'r: 's { let p: ?&'r u32 = h.item; return p; }", |
| 2415 | "record H: 'r { item: &'r mut [u32] } fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { let p: &'r [u32] = h.item; return p; }", |
| 2416 | "record H: 'r { item: &'r mut u32 } unsafe fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { let p: &'r u32 = h.item; return p; }", |
| 2417 | "record H: 'r { item: ?&'r mut u32 } unsafe fn f 'r 's (h: &'s H 'r) -> ?&'r u32 where 'r: 's { let p: ?&'r u32 = h.item; return p; }", |
| 2418 | "record H: 'r { item: &'r mut [u32] } unsafe fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { let p: &'r [u32] = h.item; return p; }", |
| 2419 | "record H: 'r { item: &'r mut u32 } fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &*h.item; }", |
| 2420 | "record H: 'r { item: &'r mut u32 } unsafe fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &*h.item; }", |
| 2421 | "record H: 'r { items: &'r mut [u32] } fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &h.items[0]; }", |
| 2422 | "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r 's (h: &'s H 'r) -> &'r u32 where 'r: 's { return &h.items[0]; }", |
| 2423 | "record H: 'r { item: &'r mut u32 } fn f 'r 's (h: &'s mut H 'r) -> &'r mut u32 where 'r: 's { return &mut *h.item; }", |
| 2424 | "record H: 'r { item: &'r mut u32 } unsafe fn f 'r 's (h: &'s mut H 'r) -> &'r mut u32 where 'r: 's { return &mut *h.item; }", |
| 2425 | "record H { item: *mut u32 } fn f(h: &H) -> *u32 { return &*h.item; }", |
| 2426 | "record H { item: *mut u32 } unsafe fn f(h: &H) -> *u32 { return &*h.item; }", |
| 2427 | "record H: 'r { items: &'r mut [u32] } fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { return &h.items[..]; }", |
| 2428 | "record H: 'r { items: &'r mut [u32] } unsafe fn f 'r 's (h: &'s H 'r) -> &'r [u32] where 'r: 's { return &h.items[..]; }", |
| 2429 | ] { |
| 2430 | let mut arena = super::testArena(); |
| 2431 | let storage: 'test = &mut arena in { |
| 2432 | let mut res = super::testResolver(storage); |
| 2433 | let result = try super::resolveProgramStr(&mut res, program); |
| 2434 | let error = try super::expectError(&result); |
| 2435 | let case resolver::ErrorKind::TypeMismatch(_) = error.kind else throw testing::TestError::Failed; |
| 2436 | } |
| 2437 | } |
| 2438 | } |
| 2439 | |
| 2440 | /// A shared binding cannot hide a move from an exclusive field. |
| 2441 | @test unsafe fn testSharedBindingPreservesSourceOwnership() throws (testing::TestError) { |
| 2442 | for program in [ |
| 2443 | "record H { item: *mut u32 } fn f(h: H) -> *u32 { let p: *u32 = h.item; return p; }", |
| 2444 | "record H { item: ?*mut u32 } fn f(h: H) -> ?*u32 { let p: ?*u32 = h.item; return p; }", |
| 2445 | "record H: 'r { item: &'r mut u32 } fn f 'r (h: &'r H 'r) -> &'r u32 { let p: &'r u32 = h.item; return p; }", |
| 2446 | "record H { item: *mut u32 } unsafe fn f(h: H) -> *u32 { let p: *u32 = h.item; return p; }", |
| 2447 | "record H { item: ?*mut u32 } unsafe fn f(h: H) -> ?*u32 { let p: ?*u32 = h.item; return p; }", |
| 2448 | "record H: 'r { item: &'r mut u32 } unsafe fn f 'r (h: &'r H 'r) -> &'r u32 { let p: &'r u32 = h.item; return p; }", |
| 2449 | ] { |
| 2450 | let mut arena = super::testArena(); |
| 2451 | let storage: 'test = &mut arena in { |
| 2452 | let mut res = super::testResolver(storage); |
| 2453 | let result = try super::resolveProgramStr(&mut res, program); |
| 2454 | try super::expectErrorKind(&result, resolver::ErrorKind::LinearPartialMove); |
| 2455 | } |
| 2456 | } |
| 2457 | } |
| 2458 | |
| 2459 | /// Methods and trait instances resolve declaration region parameters. |
| 2460 | @test unsafe fn testRegionalMethods() throws (testing::TestError) { |
| 2461 | for program in [ |
| 2462 | "record View: 'r { value: &'r u32 } fn (view: &View 'r) get 'r () -> &'r u32 { return view.value; } fn read 'x (view: &View 'x) -> u32 { return *view.get(); }", |
| 2463 | "trait Pick { fn (&Pick) pick 'r (value: &'r u32) -> &'r u32; } record Picker {} instance Pick for Picker { fn (picker: &Picker) pick 'r (value: &'r u32) -> &'r u32 { return value; } } fn choose 'x (picker: &opaque Pick, value: &'x u32) -> &'x u32 { return picker.pick(value); }", |
| 2464 | "trait Read { fn (&Read) read() -> u32; } record Regional: 'r { value: &'r u32 } instance Read for Regional 'r { fn (value: &Regional 'r) read() -> u32 { return *value.value; } } fn inspect(value: &opaque Read) -> u32 { return value.read(); } fn call 'x (value: &Regional 'x) -> u32 { return inspect(value); }", |
| 2465 | ] { |
| 2466 | let mut arena = super::testArena(); |
| 2467 | let storage: 'method = &mut arena in { |
| 2468 | let mut res = super::testResolver(storage); |
| 2469 | let result = try super::resolveProgramStr(&mut res, program); |
| 2470 | try super::expectNoErrors(&result); |
| 2471 | } |
| 2472 | } |
| 2473 | } |