Allow implicit raw-pointer borrows in unsafe contexts
f5aa0434a836a4cd4dfd4515e5848490b95f2319580c58761bf36edda871ba3e
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 | + | } |