test: Bound synthesized resolver test entry points

591e555752ba5aef9219f455d903e1d53c3d981f962874634fc2b306714ad0d7
Alexis Sellier committed ago 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 +
}