Require unsafe functions for pointer arithmetic
5807f724d98a1885f0df04b1d6f6d07ee73b023f71aca1dab1f1b0c4216e85d6
1 parent
a9d3b2cf
lib/std/lang/resolver.rad
+2 -6
| 7097 | 7097 | throw emitError(self, node, ErrorKind::OpaquePointerArithmetic); |
|
| 7098 | 7098 | } |
|
| 7099 | 7099 | if leftClass <> types::PointerClass::Ref |
|
| 7100 | 7100 | and isNumericType(rightTy) |
|
| 7101 | 7101 | { |
|
| 7102 | - | if leftClass == types::PointerClass::Unsafe { |
|
| 7103 | - | try requireUnsafe(self, node); |
|
| 7104 | - | } |
|
| 7102 | + | try requireUnsafe(self, node); |
|
| 7105 | 7103 | return setNodeType(self, node, leftTy); |
|
| 7106 | 7104 | } |
|
| 7107 | 7105 | } |
|
| 7108 | 7106 | if let case Type::Pointer { class: rightClass, target: rightTarget, .. } = rightTy { |
|
| 7109 | 7107 | if *rightTarget == Type::Opaque { |
| 7111 | 7109 | } |
|
| 7112 | 7110 | if binop.op == ast::BinaryOp::Add |
|
| 7113 | 7111 | and rightClass <> types::PointerClass::Ref |
|
| 7114 | 7112 | and isNumericType(leftTy) |
|
| 7115 | 7113 | { |
|
| 7116 | - | if rightClass == types::PointerClass::Unsafe { |
|
| 7117 | - | try requireUnsafe(self, node); |
|
| 7118 | - | } |
|
| 7114 | + | try requireUnsafe(self, node); |
|
| 7119 | 7115 | return setNodeType(self, node, rightTy); |
|
| 7120 | 7116 | } |
|
| 7121 | 7117 | } |
|
| 7122 | 7118 | } |
|
| 7123 | 7119 | let leftTy = try checkNumeric(self, binop.left); |
lib/std/lang/resolver/tests.rad
+20 -0
| 5461 | 5461 | let result = try resolveProgramStr(&mut a, program); |
|
| 5462 | 5462 | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5463 | 5463 | } |
|
| 5464 | 5464 | } |
|
| 5465 | 5465 | ||
| 5466 | + | /// Pointer offsets require an unsafe function for either operand order. |
|
| 5467 | + | @test unsafe fn testPointerArithmeticRequiresUnsafe() throws (testing::TestError) { |
|
| 5468 | + | let programs = &[ |
|
| 5469 | + | "fn run(p: *u8) -> *u8 { return p + 1; }", |
|
| 5470 | + | "fn run(p: *u8) -> *u8 { return 1 + p; }", |
|
| 5471 | + | "fn run(p: *u8) -> *u8 { return p - 1; }", |
|
| 5472 | + | "fn run(p: *mut u8) { set p += 1; }", |
|
| 5473 | + | "static DATA: [u8; 1] = [42]; fn run() -> u8 { let p = &DATA[0]; return *(p + 1); }", |
|
| 5474 | + | ]; |
|
| 5475 | + | for program in programs { |
|
| 5476 | + | let mut a = testResolver(); |
|
| 5477 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5478 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5479 | + | } |
|
| 5480 | + | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u8 { return p + 1; }"); |
|
| 5481 | + | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *u8 { return 1 + p; }"); |
|
| 5482 | + | try expectAnalyzeOk("unsafe fn run(p: *mut u8) -> *mut u8 { return p - 1; }"); |
|
| 5483 | + | try expectAnalyzeOk("fn run(value: u32) -> u32 { return value + 1; }"); |
|
| 5484 | + | } |
|
| 5485 | + | ||
| 5466 | 5486 | /// Unsafe declarations may compose unsafe operations and calls. |
|
| 5467 | 5487 | @test unsafe fn testUnsafePointerOperationsAllowed() throws (testing::TestError) { |
|
| 5468 | 5488 | let program = "record Marker: Once {} unsafe fn load(pointer: *unsafe u32) -> u32 { return *pointer; } unsafe fn run(pointer: *unsafe u32) -> u32 { let next = pointer + 1; let same = pointer == next; return load(pointer); }"; |
|
| 5469 | 5489 | try expectAnalyzeOk(program); |
|
| 5470 | 5490 | } |
test/tests/pointer.arithmetic.unsafe.rad
added
+16 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | /// Backing storage for pointer offsets. |
|
| 4 | + | static DATA: [u8; 3] = [11, 22, 33]; |
|
| 5 | + | ||
| 6 | + | /// Read and write valid offsets in an unsafe function. |
|
| 7 | + | @default unsafe fn main() -> i32 { |
|
| 8 | + | let p = &mut DATA[0]; |
|
| 9 | + | let q = p + 1; |
|
| 10 | + | if *q <> 22 { return 1; } |
|
| 11 | + | if *(1 + q) <> 33 { return 2; } |
|
| 12 | + | if *(q - 1) <> 11 { return 3; } |
|
| 13 | + | set *q = 44; |
|
| 14 | + | if DATA[1] <> 44 { return 4; } |
|
| 15 | + | return 0; |
|
| 16 | + | } |