test: Bound synthesized resolver test entry points
591e555752ba5aef9219f455d903e1d53c3d981f962874634fc2b306714ad0d7
1 parent
bf4dc3d2
lib/std/lang/resolver/tests.rad
+26 -11
| 6343 | 6343 | let result = try resolveProgramStr(&mut a, "let n: u32 = 1; let p: &u32 = &n;"); |
|
| 6344 | 6344 | try expectErrorKind(&result, super::ErrorKind::InvalidRefPosition); |
|
| 6345 | 6345 | } |
|
| 6346 | 6346 | ||
| 6347 | 6347 | /// Invalid calls in function bodies retain their resolution diagnostics. |
|
| 6348 | - | @test unsafe fn testInvalidBodyCalls() throws (testing::TestError) { |
|
| 6348 | + | unsafe fn testInvalidBodyCalls() throws (testing::TestError) { |
|
| 6349 | 6349 | for program in &[ |
|
| 6350 | 6350 | "fn f(x: u32) {} fn g() { f(1, 2); }", |
|
| 6351 | 6351 | "fn f(x: u32) {} fn g() { if true { f(1, 2); } }", |
|
| 6352 | 6352 | "fn f(x: u32) {} unsafe fn g() { f(1, 2); }", |
|
| 6353 | 6353 | "fn f(x: u32) -> u32 { return x; } fn g() { f(f(1, 2)); }", |
| 6359 | 6359 | })); |
|
| 6360 | 6360 | } |
|
| 6361 | 6361 | } |
|
| 6362 | 6362 | ||
| 6363 | 6363 | /// Unsafe calls with excess arguments report the unsafe-call diagnostic. |
|
| 6364 | - | @test unsafe fn testInvalidUnsafeBodyCall() throws (testing::TestError) { |
|
| 6364 | + | unsafe fn testInvalidUnsafeBodyCall() throws (testing::TestError) { |
|
| 6365 | 6365 | let mut res = testResolver(); |
|
| 6366 | 6366 | let result = try resolveProgramStr(&mut res, |
|
| 6367 | 6367 | "unsafe fn f(x: u32) {} fn g() { f(1, 2); }"); |
|
| 6368 | 6368 | try expectErrorKind(&result, super::ErrorKind::UnsafeCall); |
|
| 6369 | 6369 | } |
|
| 6370 | 6370 | ||
| 6371 | 6371 | /// Iteration over raw slices requires permission to read their storage. |
|
| 6372 | - | @test unsafe fn testRawSliceIterationRequiresUnsafe() throws (testing::TestError) { |
|
| 6372 | + | unsafe fn testRawSliceIterationRequiresUnsafe() throws (testing::TestError) { |
|
| 6373 | 6373 | for program in [ |
|
| 6374 | 6374 | "fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }", |
|
| 6375 | 6375 | "fn f(p: *unsafe mut [u32]) { for item, index in p { assert item == index; } }", |
|
| 6376 | 6376 | "record R { items: *unsafe [u32] } fn f(r: &R) { for item in r.items { assert item == 0; } }", |
|
| 6377 | 6377 | ] { |
| 6380 | 6380 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 6381 | 6381 | } |
|
| 6382 | 6382 | } |
|
| 6383 | 6383 | ||
| 6384 | 6384 | /// Checked iteration is safe, and raw iteration accepts explicit permission. |
|
| 6385 | - | @test unsafe fn testSliceIterationPermissions() throws (testing::TestError) { |
|
| 6385 | + | unsafe fn testSliceIterationPermissions() throws (testing::TestError) { |
|
| 6386 | 6386 | for program in [ |
|
| 6387 | 6387 | "unsafe fn f(p: *unsafe [u32]) { for item in p { assert item == 0; } }", |
|
| 6388 | 6388 | "fn f(p: *unsafe [u32]) { unsafe { for item in p { assert item == 0; } } }", |
|
| 6389 | 6389 | "fn f(p: &[u32]) { for item in p { assert item == 0; } }", |
|
| 6390 | 6390 | "fn f(p: &mut [u32]) { for item, index in p { assert item == index; } }", |
| 6395 | 6395 | try expectNoErrors(&result); |
|
| 6396 | 6396 | } |
|
| 6397 | 6397 | } |
|
| 6398 | 6398 | ||
| 6399 | 6399 | /// Short-circuit paths must agree on exact-use ownership. |
|
| 6400 | - | @test unsafe fn testShortCircuitOwnership() throws (testing::TestError) { |
|
| 6400 | + | unsafe fn testShortCircuitOwnership() throws (testing::TestError) { |
|
| 6401 | 6401 | for program in [ |
|
| 6402 | 6402 | "union Ticket: Once { Value(u32) } fn take(t: Ticket) -> bool { match t { case Ticket::Value(n) => return n == 1, } } fn f(t: Ticket) { let result = false and take(t); }", |
|
| 6403 | 6403 | "union Ticket: Once { Value(u32) } fn take(t: Ticket) -> bool { match t { case Ticket::Value(n) => return n == 1, } } fn f(t: Ticket) { let result = true or take(t); }", |
|
| 6404 | 6404 | "union Ticket: Once { Value(u32) } fn take(t: Ticket) -> bool { match t { case Ticket::Value(n) => return n == 1, } } fn f(t: Ticket, flag: bool) { let result = flag and take(t); }", |
|
| 6405 | 6405 | "union Ticket: Once { Value(u32) } fn take(t: Ticket) -> bool { match t { case Ticket::Value(n) => return n == 1, } } unsafe fn f(t: Ticket, flag: bool) { let result = flag or take(t); }", |
| 6411 | 6411 | else throw testing::TestError::Failed; |
|
| 6412 | 6412 | } |
|
| 6413 | 6413 | } |
|
| 6414 | 6414 | ||
| 6415 | 6415 | /// The left operand executes on every short-circuit path. |
|
| 6416 | - | @test unsafe fn testShortCircuitLeftConsumption() throws (testing::TestError) { |
|
| 6416 | + | unsafe fn testShortCircuitLeftConsumption() throws (testing::TestError) { |
|
| 6417 | 6417 | for program in [ |
|
| 6418 | 6418 | "union Ticket: Once { Value(u32) } fn take(t: Ticket) -> bool { match t { case Ticket::Value(n) => return n == 1, } } fn f(t: Ticket) { let result = take(t) and false; }", |
|
| 6419 | 6419 | "union Ticket: Once { Value(u32) } fn take(t: Ticket) -> bool { match t { case Ticket::Value(n) => return n == 1, } } fn f(t: Ticket) { let result = take(t) or true; }", |
|
| 6420 | 6420 | ] { |
|
| 6421 | 6421 | let mut res = testResolver(); |
| 6423 | 6423 | try expectNoErrors(&result); |
|
| 6424 | 6424 | } |
|
| 6425 | 6425 | } |
|
| 6426 | 6426 | ||
| 6427 | 6427 | /// Earlier reference arguments protect their storage during later arguments. |
|
| 6428 | - | @test unsafe fn testCallArgumentLoans() throws (testing::TestError) { |
|
| 6428 | + | unsafe fn testCallArgumentLoans() throws (testing::TestError) { |
|
| 6429 | 6429 | for program in [ |
|
| 6430 | 6430 | "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &mut u32, n: u32) {} fn f (p: &mut u32) { outer(&mut *p, inner(p)); }", |
|
| 6431 | 6431 | "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &u32, n: u32) {} fn f (p: &mut u32) { outer(&*p, inner(p)); }", |
|
| 6432 | 6432 | "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &mut u32, n: u32) {} unsafe fn f (p: &mut u32) { outer(&mut *p, inner(p)); }", |
|
| 6433 | 6433 | "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: &mut R) { (&mut *r).call(inner(&mut r.n)); }", |
| 6439 | 6439 | else throw testing::TestError::Failed; |
|
| 6440 | 6440 | } |
|
| 6441 | 6441 | } |
|
| 6442 | 6442 | ||
| 6443 | 6443 | /// Conditional explicit arguments protect every possible borrowed place. |
|
| 6444 | - | @test unsafe fn testConditionalCallArgumentLoans() throws (testing::TestError) { |
|
| 6444 | + | unsafe fn testConditionalCallArgumentLoans() throws (testing::TestError) { |
|
| 6445 | 6445 | for program in [ |
|
| 6446 | 6446 | "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &u32, n: u32) {} fn f (p: &mut u32, q: &mut u32, flag: bool) { outer(&*p if flag else &*q, inner(p)); }", |
|
| 6447 | 6447 | "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &u32, n: u32) {} fn f (p: &mut u32, q: &mut u32, flag: bool) { outer(&*p if flag else &*q, inner(q)); }", |
|
| 6448 | 6448 | "fn outer(p: &mut u32, q: &mut u32) {} fn f (p: &mut u32, q: &mut u32, flag: bool) { outer(&mut *p if flag else &mut *q, &mut *p); }", |
|
| 6449 | 6449 | "fn inner(p: &mut u32) -> u32 { set *p = 2; return 0; } fn outer(p: &u32, n: u32) {} unsafe fn f (p: &mut u32, q: &mut u32, flag: bool) { outer(&*p if flag else &*q, inner(q)); }", |
| 6458 | 6458 | else throw testing::TestError::Failed; |
|
| 6459 | 6459 | } |
|
| 6460 | 6460 | } |
|
| 6461 | 6461 | ||
| 6462 | 6462 | /// Conditional argument alternatives cannot overlap another exclusive argument. |
|
| 6463 | - | @test unsafe fn testConditionalCallArgumentOverlap() throws (testing::TestError) { |
|
| 6463 | + | unsafe fn testConditionalCallArgumentOverlap() throws (testing::TestError) { |
|
| 6464 | 6464 | for program in [ |
|
| 6465 | 6465 | "fn outer(p: &mut u32, q: &mut u32) {} fn f (p: &mut u32, q: &mut u32, flag: bool) { outer(p if flag else q, p); }", |
|
| 6466 | 6466 | "fn outer(p: &mut u32, q: &mut u32) {} fn f (p: &mut u32, q: &mut u32, flag: bool) { outer(p if flag else q, q); }", |
|
| 6467 | 6467 | "fn outer(p: &mut u32, q: &mut u32) {} fn f (p: &mut u32, q: &mut u32, flag: bool) { outer(p, p if flag else q); }", |
|
| 6468 | 6468 | "fn outer(p: &mut u32, q: &mut u32) {} fn f (p: &mut u32, q: &mut u32, flag: bool) { outer(q, p if flag else q); }", |
| 6481 | 6481 | else throw testing::TestError::Failed; |
|
| 6482 | 6482 | } |
|
| 6483 | 6483 | } |
|
| 6484 | 6484 | ||
| 6485 | 6485 | /// Conditional argument alternatives permit shared access and disjoint places. |
|
| 6486 | - | @test unsafe fn testConditionalCallArgumentSeparation() throws (testing::TestError) { |
|
| 6486 | + | unsafe fn testConditionalCallArgumentSeparation() throws (testing::TestError) { |
|
| 6487 | 6487 | for program in [ |
|
| 6488 | 6488 | "fn outer(p: &u32, q: &u32) {} fn f (p: &u32, q: &u32, flag: bool) { outer(p if flag else q, p); }", |
|
| 6489 | 6489 | "fn outer(p: &mut u32, q: &mut u32) {} fn f (p: &mut u32, q: &mut u32, r: &mut u32, flag: bool) { outer(p if flag else q, r); }", |
|
| 6490 | 6490 | "record R { a: u32, b: u32, c: u32 } fn outer(p: &mut u32, q: &mut u32) {} fn f (r: &mut R, flag: bool) { outer(&mut r.a if flag else &mut r.b, &mut r.c); }", |
|
| 6491 | 6491 | "record R { n: u32 } fn (r: &mut R) call(p: &R) {} fn f (p: &mut R, q: &mut R, r: &R, flag: bool) { (p if flag else q).call(r); }", |
| 6495 | 6495 | try expectNoErrors(&result); |
|
| 6496 | 6496 | } |
|
| 6497 | 6497 | } |
|
| 6498 | 6498 | ||
| 6499 | 6499 | /// Call loans allow shared reads, separate fields, and access after the call. |
|
| 6500 | - | @test unsafe fn testCallArgumentLoanScopes() throws (testing::TestError) { |
|
| 6500 | + | unsafe fn testCallArgumentLoanScopes() throws (testing::TestError) { |
|
| 6501 | 6501 | for program in [ |
|
| 6502 | 6502 | "fn read(p: &u32) -> u32 { return *p; } fn outer(p: &u32, n: u32) {} fn f (p: &mut u32, q: &mut u32, flag: bool) { outer(&*p if flag else &*q, read(p)); set *p = 3; set *q = 4; }", |
|
| 6503 | 6503 | "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: &mut R, flag: bool) { outer(&mut r.a if flag else &mut r.b, inner(&mut r.c)); set r.a = 3; }", |
|
| 6504 | 6504 | "fn outer(p: &mut u32, n: u32) {} fn f (p: &mut u32, flag: bool) { outer(&mut *p if flag else &mut *p, 0); set *p = 3; }", |
|
| 6505 | 6505 | "fn read(p: &u32) -> u32 { return *p; } fn outer(p: &u32, n: u32) {} fn f (p: &mut u32) { outer(&*p, read(p)); set *p = 3; }", |
| 6508 | 6508 | let mut res = testResolver(); |
|
| 6509 | 6509 | let result = try resolveProgramStr(&mut res, program); |
|
| 6510 | 6510 | try expectNoErrors(&result); |
|
| 6511 | 6511 | } |
|
| 6512 | 6512 | } |
|
| 6513 | + | ||
| 6514 | + | /// Verify call resolution, borrow protection, and ownership paths. |
|
| 6515 | + | @test unsafe fn testExtractedSafetyChecks() throws (testing::TestError) { |
|
| 6516 | + | try testInvalidBodyCalls(); |
|
| 6517 | + | try testInvalidUnsafeBodyCall(); |
|
| 6518 | + | try testRawSliceIterationRequiresUnsafe(); |
|
| 6519 | + | try testSliceIterationPermissions(); |
|
| 6520 | + | try testShortCircuitOwnership(); |
|
| 6521 | + | try testShortCircuitLeftConsumption(); |
|
| 6522 | + | try testCallArgumentLoans(); |
|
| 6523 | + | try testConditionalCallArgumentLoans(); |
|
| 6524 | + | try testConditionalCallArgumentOverlap(); |
|
| 6525 | + | try testConditionalCallArgumentSeparation(); |
|
| 6526 | + | try testCallArgumentLoanScopes(); |
|
| 6527 | + | } |