Allow implicit raw-pointer borrows in unsafe contexts

f5aa0434a836a4cd4dfd4515e5848490b95f2319580c58761bf36edda871ba3e
Alexis Sellier committed ago 1 parent 7397ed17
lib/std/lang/resolver.rad +11 -6
1768 1768
        }
1769 1769
    }
1770 1770
    return true;
1771 1771
}
1772 1772
1773 -
/// Return whether pointer classes are compatible.
1773 +
/// Return whether pointer classes are compatible in the current safety context.
1774 1774
fn pointerClassesAssignable(
1775 1775
    to: types::PointerClass,
1776 1776
    from: types::PointerClass,
1777 +
    inUnsafeContext: bool,
1777 1778
) -> bool {
1778 1779
    return to == from or (
1779 1780
        to == types::PointerClass::Ref
1780 -
        and from == types::PointerClass::Owned
1781 +
        and (from == types::PointerClass::Owned
1782 +
            or (from == types::PointerClass::Unsafe and inUnsafeContext))
1781 1783
    );
1782 1784
}
1783 1785
1784 1786
/// Check if the `from` type is assignable to the `to` type, and return a
1785 1787
/// coercion plan if so.
1803 1805
        return Coercion::Identity;
1804 1806
    }
1805 1807
    if let case Type::Pointer { class: lhsClass, target: lhsTarget, mutable: lhsMutable } = to {
1806 1808
        let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from
1807 1809
            else return nil;
1808 -
        if not pointerClassesAssignable(lhsClass, rhsClass) {
1810 +
        if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext) {
1809 1811
            return nil;
1810 1812
        }
1811 1813
        // Allow coercion from `*T` to `*opaque`, and mutable counterparts.
1812 1814
        if *lhsTarget == Type::Opaque {
1813 1815
            if lhsMutable and not rhsMutable {
1823 1825
        }
1824 1826
        return nil;
1825 1827
    }
1826 1828
    if let case Type::TraitObject { class: lhsClass, traitInfo: lhsTraitInfo, mutable: lhsMutable } = to {
1827 1829
        if let case Type::Pointer { class: rhsClass, target: rhsTarget, mutable: rhsMutable } = from {
1828 -
            if not pointerClassesAssignable(lhsClass, rhsClass)
1830 +
            if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext)
1829 1831
                or (lhsMutable and not rhsMutable)
1830 1832
            {
1831 1833
                return nil;
1832 1834
            }
1833 1835
            if let inst = findInstance(self, lhsTraitInfo, *rhsTarget) {
1834 1836
                return Coercion::TraitObject { traitInfo: lhsTraitInfo, inst };
1835 1837
            }
1836 1838
        }
1837 1839
        if let case Type::TraitObject { class: rhsClass, traitInfo: rhsTraitInfo, mutable: rhsMutable } = from {
1838 -
            if not pointerClassesAssignable(lhsClass, rhsClass)
1840 +
            if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext)
1839 1841
                or lhsTraitInfo <> rhsTraitInfo
1840 1842
            {
1841 1843
                return nil;
1842 1844
            }
1843 1845
            if lhsMutable and not rhsMutable {
1848 1850
        return nil;
1849 1851
    }
1850 1852
    if let case Type::Slice { class: lhsClass, item: lhsItem, mutable: lhsMutable } = to {
1851 1853
        let case Type::Slice { class: rhsClass, item: rhsItem, mutable: rhsMutable } = from
1852 1854
            else return nil;
1853 -
        if not pointerClassesAssignable(lhsClass, rhsClass)
1855 +
        if not pointerClassesAssignable(lhsClass, rhsClass, self.inUnsafeContext)
1854 1856
            or (lhsMutable and not rhsMutable)
1855 1857
        {
1856 1858
            return nil;
1857 1859
        }
1858 1860
        // Allow coercion from `*[T]` to `*[opaque]`, and mutable counterparts.
2195 2197
}
2196 2198
2197 2199
/// Check if the `from` type is assignable to the `to` type, and return a
2198 2200
/// coercion plan if so, or throw an error if not.
2199 2201
unsafe fn expectAssignable(self: &mut Resolver, to: Type, from: Type, site: *ast::Node) -> Coercion throws (ResolveError) {
2202 +
    if isRefType(to) and isUnsafePointerType(from) {
2203 +
        try requireUnsafe(self, site);
2204 +
    }
2200 2205
    // Ensure any nested nominal types are resolved before checking assignability.
2201 2206
    try ensureTypeResolved(self, to, site);
2202 2207
    if let coercion = isAssignable(self, to, from, site) {
2203 2208
        return setNodeCoercion(self, site, coercion);
2204 2209
    }
lib/std/lang/resolver/tests.rad +60 -0
5342 5342
@test unsafe fn testStackBorrowAllowed() throws (testing::TestError) {
5343 5343
    try expectAnalyzeOk("fn read(value: &u32) -> u32 { return *value; } fn run() -> u32 { let value: u32 = 7; return read(&value); }");
5344 5344
    try expectAnalyzeOk("fn write(values: &mut [u32]) { set values[0] = 7; } fn run() { let mut values: [u32; 2] = [1, 2]; write(&mut values[..]); }");
5345 5345
}
5346 5346
5347 +
/// Raw pointers and slices can be borrowed in an unsafe context.
5348 +
@test unsafe fn testImplicitRawBorrowAllowed() throws (testing::TestError) {
5349 +
    let programs = &[
5350 +
        "fn read(p: &u32) -> u32 { return *p; } unsafe fn run(p: *unsafe u32) -> u32 { return read(p); }",
5351 +
        "fn write(p: &mut u32) { set *p = 7; } fn run(p: *unsafe mut u32) { unsafe { write(p); write(p); } }",
5352 +
        "fn read(p: &[u32]) -> u32 { return p[0]; } unsafe fn run(p: *unsafe [u32]) -> u32 { return read(p); }",
5353 +
        "fn write(p: &mut [u32]) { set p[0] = 7; } unsafe fn run(p: *unsafe mut [u32]) { write(p); write(p); }",
5354 +
        "fn read(p: &u32) {} unsafe fn run(p: *unsafe mut u32) { read(p); }",
5355 +
        "trait Read { fn (&Read) get(); } fn read(p: &opaque Read) {} unsafe fn run(p: *unsafe opaque Read) { read(p); }",
5356 +
    ];
5357 +
    for program in programs {
5358 +
        try expectAnalyzeOk(program);
5359 +
    }
5360 +
}
5361 +
5362 +
/// An implicit borrow of raw storage requires an unsafe context.
5363 +
@test unsafe fn testImplicitRawBorrowRequiresUnsafe() throws (testing::TestError) {
5364 +
    let programs = &[
5365 +
        "fn read(p: &u32) {} fn run(p: *unsafe u32) { read(p); }",
5366 +
        "fn write(p: &mut u32) {} fn run(p: *unsafe mut u32) { write(p); }",
5367 +
        "fn read(p: &[u32]) {} fn run(p: *unsafe [u32]) { read(p); }",
5368 +
        "fn write(p: &mut [u32]) {} fn run(p: *unsafe mut [u32]) { write(p); }",
5369 +
        "trait Read { fn (&Read) get(); } fn read(p: &opaque Read) {} fn run(p: *unsafe opaque Read) { read(p); }",
5370 +
        "fn read(p: &u32) {} fn run(p: *unsafe u32) { unsafe { read(p); } read(p); }",
5371 +
    ];
5372 +
    for program in programs {
5373 +
        let mut a = testResolver();
5374 +
        let result = try resolveProgramStr(&mut a, program);
5375 +
        try expectErrorKind(&result, super::ErrorKind::UnsafeOperation);
5376 +
    }
5377 +
}
5378 +
5379 +
/// Raw borrows preserve mutability, storage type, and ownership constraints.
5380 +
@test unsafe fn testImplicitRawBorrowPreservesTypes() throws (testing::TestError) {
5381 +
    let programs = &[
5382 +
        "fn write(p: &mut u32) {} unsafe fn run(p: *unsafe u32) { write(p); }",
5383 +
        "fn write(p: &mut [u32]) {} unsafe fn run(p: *unsafe [u32]) { write(p); }",
5384 +
        "fn read(p: &u64) {} unsafe fn run(p: *unsafe u32) { read(p); }",
5385 +
        "fn read(p: &[u64]) {} unsafe fn run(p: *unsafe [u32]) { read(p); }",
5386 +
        "fn take(p: *u32) {} unsafe fn run(p: *unsafe u32) { take(p); }",
5387 +
        "fn take(p: *[u32]) {} unsafe fn run(p: *unsafe [u32]) { take(p); }",
5388 +
        "fn read(p: &*u32) {} unsafe fn run(p: *unsafe *unsafe u32) { read(p); }",
5389 +
    ];
5390 +
    for program in programs {
5391 +
        let mut a = testResolver();
5392 +
        let result = try resolveProgramStr(&mut a, program);
5393 +
        let err = try expectError(&result);
5394 +
        let case super::ErrorKind::TypeMismatch(_) = err.kind
5395 +
            else throw testing::TestError::Failed;
5396 +
    }
5397 +
}
5398 +
5399 +
/// Implicit raw borrows retain the call's exclusive-borrow checks.
5400 +
@test unsafe fn testImplicitRawBorrowConflict() throws (testing::TestError) {
5401 +
    let mut a = testResolver();
5402 +
    let result = try resolveProgramStr(&mut a,
5403 +
        "fn useBoth(a: &mut u32, b: &u32) {} unsafe fn run(p: *unsafe mut u32) { useBoth(p, p); }");
5404 +
    try expectErrorKind(&result, super::ErrorKind::BorrowConflict("p"));
5405 +
}
5406 +
5347 5407
/// Unsafe declarations can store raw pointers to stack values.
5348 5408
@test unsafe fn testUnsafeStackPointerAllowed() throws (testing::TestError) {
5349 5409
    try expectAnalyzeOk("unsafe fn run() { let mut value: u32 = 0; let pointer: *unsafe mut u32 = &mut value as *unsafe mut u32; set *pointer = 7; }");
5350 5410
    try expectAnalyzeOk("unsafe fn run() { let mut values: [u32; 2] = [1, 2]; let slice: *unsafe mut [u32] = &mut values[..] as *unsafe mut [u32]; set slice[0] = 7; }");
5351 5411
    try expectAnalyzeOk("unsafe fn run() { let value: u32 = 7; let pointer: *unsafe u32 = &value; }");
test/tests/pointer.borrow.unsafe.rad added +71 -0
1 +
//! returns: 0
2 +
//! Borrow raw storage for safe function calls inside an unsafe context.
3 +
4 +
/// Read a borrowed value.
5 +
fn read(value: &u32) -> u32 {
6 +
    return *value;
7 +
}
8 +
9 +
/// Write a borrowed value.
10 +
fn write(value: &mut u32, number: u32) {
11 +
    set *value = number;
12 +
}
13 +
14 +
/// Write both elements of the borrowed test slice.
15 +
fn fill(values: &mut [u32]) {
16 +
    set values[0] = 10;
17 +
    set values[1] = 20;
18 +
}
19 +
20 +
/// Add both elements of the borrowed test slice.
21 +
fn sum(values: &[u32]) -> u32 {
22 +
    return values[0] + values[1];
23 +
}
24 +
25 +
/// Value inspected through a borrowed trait object.
26 +
record Cell {
27 +
    /// Number returned by the reader.
28 +
    value: u32,
29 +
}
30 +
31 +
/// Read a number through a borrowed receiver.
32 +
trait Reader {
33 +
    /// Return the stored number.
34 +
    fn (&Reader) get() -> u32;
35 +
}
36 +
37 +
instance Reader for Cell {
38 +
    /// Return the cell's stored number.
39 +
    fn (cell: &Cell) get() -> u32 {
40 +
        return cell.value;
41 +
    }
42 +
}
43 +
44 +
/// Read through an erased borrowed receiver.
45 +
fn inspect(reader: &opaque Reader) -> u32 {
46 +
    return reader.get();
47 +
}
48 +
49 +
/// Keep raw storage live for each borrowed call.
50 +
@default unsafe fn main() -> u32 {
51 +
    let mut value: u32 = 0;
52 +
    let raw: *unsafe mut u32 = &mut value;
53 +
    let alias = raw;
54 +
    write(raw, 42);
55 +
    assert read(alias) == 42;
56 +
    write(alias, 43);
57 +
    assert read(raw) == 43;
58 +
59 +
    let mut values: [u32; 2] = [0, 0];
60 +
    let rawValues: *unsafe mut [u32] = &mut values[..];
61 +
    fill(rawValues);
62 +
    assert sum(rawValues) == 30;
63 +
    assert sum(@sliceOf(&values[0] as *unsafe u32, 2)) == 30;
64 +
65 +
    let cell = Cell { value: 7 };
66 +
    let rawCell: *unsafe Cell = &cell;
67 +
    assert inspect(rawCell) == 7;
68 +
    let object: *unsafe opaque Reader = rawCell;
69 +
    assert inspect(object) == 7;
70 +
    return 0;
71 +
}