Require unsafe functions for explicit slice construction
d3cc6c5fbbc2e0864036c767efba5b0ce50ce05eba9f8600fc1ec9e347431e32
1 parent
ceaf9422
lib/std/arch/rv64/emit.rad
+1 -1
| 343 | 343 | /// |
|
| 344 | 344 | /// Called after each function. |
|
| 345 | 345 | /// |
|
| 346 | 346 | /// Uses two-instruction sequences: short branches use `branch` and `nop`, |
|
| 347 | 347 | /// long branches use inverted branch and `jal` or `auipc` and `jalr`. |
|
| 348 | - | export fn patchLocalBranches(e: &mut Emitter) { |
|
| 348 | + | export unsafe fn patchLocalBranches(e: &mut Emitter) { |
|
| 349 | 349 | for i in 0..e.pendingBranches.len { |
|
| 350 | 350 | let p = e.pendingBranches[i]; |
|
| 351 | 351 | let offset = labels::branchToBlock(&e.labels, p.index, p.target, super::INSTR_SIZE); |
|
| 352 | 352 | match p.kind { |
|
| 353 | 353 | case BranchKind::Cond { op, rs1, rs2 } => { |
lib/std/lang/alloc.rad
+1 -1
| 93 | 93 | /// Allocate a slice of `count` elements, each of `size` bytes with given alignment. |
|
| 94 | 94 | /// |
|
| 95 | 95 | /// Returns a type-erased slice that should be cast to the appropriate `*[T]`. |
|
| 96 | 96 | /// The slice length is set to `count` (element count, not bytes). |
|
| 97 | 97 | /// Throws `AllocError` if the arena is exhausted. |
|
| 98 | - | export fn allocSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
|
| 98 | + | export unsafe fn allocSlice(arena: &mut Arena, size: u32, alignment: u32, count: u32) -> *mut [opaque] throws (AllocError) { |
|
| 99 | 99 | if count == 0 { |
|
| 100 | 100 | return &mut []; |
|
| 101 | 101 | } |
|
| 102 | 102 | let ptr = try alloc(arena, size * count, alignment); |
|
| 103 | 103 |
lib/std/lang/lower.rad
+2 -2
| 1946 | 1946 | return sym; |
|
| 1947 | 1947 | } |
|
| 1948 | 1948 | ||
| 1949 | 1949 | /// Remove the last block parameter and its associated variable. |
|
| 1950 | 1950 | /// Used when detecting a trivial phi that can be eliminated. |
|
| 1951 | - | fn removeLastBlockParam(self: &mut FnLowerer, block: BlockId) { |
|
| 1951 | + | unsafe fn removeLastBlockParam(self: &mut FnLowerer, block: BlockId) { |
|
| 1952 | 1952 | let blk = getBlockMut(self, block); |
|
| 1953 | 1953 | if blk.params.len > 0 { |
|
| 1954 | 1954 | // TODO: Use `pop`? |
|
| 1955 | 1955 | set blk.params = @sliceOf(blk.params.ptr, blk.params.len - 1, blk.params.cap); |
|
| 1956 | 1956 | } |
| 2868 | 2868 | fn enterVarScope(self: &FnLowerer) -> u32 { |
|
| 2869 | 2869 | return self.vars.len; |
|
| 2870 | 2870 | } |
|
| 2871 | 2871 | ||
| 2872 | 2872 | /// Restore lexical variable scope depth. |
|
| 2873 | - | fn exitVarScope(self: &mut FnLowerer, savedVarsLen: u32) { |
|
| 2873 | + | unsafe fn exitVarScope(self: &mut FnLowerer, savedVarsLen: u32) { |
|
| 2874 | 2874 | set self.vars = @sliceOf(self.vars.ptr, savedVarsLen, self.vars.cap); |
|
| 2875 | 2875 | } |
|
| 2876 | 2876 | ||
| 2877 | 2877 | /// Get the metadata for a variable. |
|
| 2878 | 2878 | fn getVar(self: &FnLowerer, v: Var) -> *VarData { |
lib/std/lang/resolver.rad
+18 -17
| 1039 | 1039 | } |
|
| 1040 | 1040 | return &errs[index]; |
|
| 1041 | 1041 | } |
|
| 1042 | 1042 | ||
| 1043 | 1043 | /// Record an error diagnostic and return an error sentinel suitable for throwing. |
|
| 1044 | - | fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError { |
|
| 1044 | + | unsafe fn emitError(self: &mut Resolver, node: ?*ast::Node, kind: ErrorKind) -> ResolveError { |
|
| 1045 | 1045 | // If our error list is full, just return an error without recording it. |
|
| 1046 | 1046 | if self.errors.len >= self.errors.cap { |
|
| 1047 | 1047 | return ResolveError::Failure; |
|
| 1048 | 1048 | } |
|
| 1049 | 1049 | // Don't record more than one error per node. |
| 1056 | 1056 | ||
| 1057 | 1057 | return ResolveError::Failure; |
|
| 1058 | 1058 | } |
|
| 1059 | 1059 | ||
| 1060 | 1060 | /// Like [`emitError`], but for type mismatches specifically. |
|
| 1061 | - | fn emitTypeMismatch(self: &mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError { |
|
| 1061 | + | unsafe fn emitTypeMismatch(self: &mut Resolver, node: ?*ast::Node, mismatch: TypeMismatch) -> ResolveError { |
|
| 1062 | 1062 | return emitError(self, node, ErrorKind::TypeMismatch(mismatch)); |
|
| 1063 | 1063 | } |
|
| 1064 | 1064 | ||
| 1065 | 1065 | /// Allocate a scope object with the given symbol capacity. |
|
| 1066 | 1066 | unsafe fn allocScope(self: &mut Resolver, owner: *ast::Node, capacity: u32) -> *mut Scope { |
| 1156 | 1156 | } |
|
| 1157 | 1157 | return Type::Never; |
|
| 1158 | 1158 | } |
|
| 1159 | 1159 | ||
| 1160 | 1160 | /// Require that loop control statements appear inside a loop. |
|
| 1161 | - | fn ensureInsideLoop(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 1161 | + | unsafe fn ensureInsideLoop(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 1162 | 1162 | if self.loopDepth == 0 { |
|
| 1163 | 1163 | throw emitError(self, node, ErrorKind::InvalidLoopControl); |
|
| 1164 | 1164 | } |
|
| 1165 | 1165 | } |
|
| 1166 | 1166 |
| 1196 | 1196 | set self.currentFn = nil; |
|
| 1197 | 1197 | exitScope(self); |
|
| 1198 | 1198 | } |
|
| 1199 | 1199 | ||
| 1200 | 1200 | /// Extract the identifier text from a node. |
|
| 1201 | - | fn nodeName(self: &mut Resolver, node: *ast::Node) -> *[u8] |
|
| 1201 | + | unsafe fn nodeName(self: &mut Resolver, node: *ast::Node) -> *[u8] |
|
| 1202 | 1202 | throws (ResolveError) |
|
| 1203 | 1203 | { |
|
| 1204 | 1204 | let case ast::NodeValue::Ident(name) = node.value |
|
| 1205 | 1205 | else throw emitError(self, node, ErrorKind::ExpectedIdentifier); |
|
| 1206 | 1206 | return name; |
| 2240 | 2240 | ||
| 2241 | 2241 | return sym; |
|
| 2242 | 2242 | } |
|
| 2243 | 2243 | ||
| 2244 | 2244 | /// Add a symbol to the given scope. |
|
| 2245 | - | fn addSymbolToScope(self: &mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) { |
|
| 2245 | + | unsafe fn addSymbolToScope(self: &mut Resolver, sym: *mut Symbol, scope: *mut Scope, site: *ast::Node) throws (ResolveError) { |
|
| 2246 | 2246 | for i in 0..scope.symbolsLen { |
|
| 2247 | 2247 | if scope.symbols[i].name == sym.name { |
|
| 2248 | 2248 | throw emitError(self, site, ErrorKind::DuplicateBinding(sym.name)); |
|
| 2249 | 2249 | } |
|
| 2250 | 2250 | } |
| 2413 | 2413 | } |
|
| 2414 | 2414 | ||
| 2415 | 2415 | /// Flatten an identifier or scope access chain into an array of name segments. |
|
| 2416 | 2416 | /// Examples: `fnord` -> `&["fnord"]`, `a::b::c` -> `&["a", "b", "c"]`. |
|
| 2417 | 2417 | /// Return the number of segments written to the buffer. |
|
| 2418 | - | fn flattenPath( |
|
| 2418 | + | unsafe fn flattenPath( |
|
| 2419 | 2419 | self: &mut Resolver, |
|
| 2420 | 2420 | node: *ast::Node, |
|
| 2421 | 2421 | buf: &mut [*[u8]] |
|
| 2422 | 2422 | ) -> u32 throws (ResolveError) { |
|
| 2423 | 2423 | let mut out: u32 = 0; |
| 2629 | 2629 | ||
| 2630 | 2630 | return try resolveModulePathRecursive(self, module, &pathBuf[1..pathLen], sym); |
|
| 2631 | 2631 | } |
|
| 2632 | 2632 | ||
| 2633 | 2633 | /// Recursively resolve the remaining path segments by traversing child modules. |
|
| 2634 | - | fn resolveModulePathRecursive( |
|
| 2634 | + | unsafe fn resolveModulePathRecursive( |
|
| 2635 | 2635 | self: &mut Resolver, |
|
| 2636 | 2636 | node: *ast::Node, |
|
| 2637 | 2637 | path: &[*[u8]], |
|
| 2638 | 2638 | sym: *Symbol |
|
| 2639 | 2639 | ) -> ResolvedModule throws (ResolveError) { |
| 2712 | 2712 | } |
|
| 2713 | 2713 | } |
|
| 2714 | 2714 | } |
|
| 2715 | 2715 | ||
| 2716 | 2716 | /// Require the current function to be unsafe. |
|
| 2717 | - | fn requireUnsafe(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 2717 | + | unsafe fn requireUnsafe(self: &mut Resolver, node: *ast::Node) throws (ResolveError) { |
|
| 2718 | 2718 | if not self.inUnsafeFn { |
|
| 2719 | 2719 | throw emitError(self, node, ErrorKind::UnsafeOperation); |
|
| 2720 | 2720 | } |
|
| 2721 | 2721 | } |
|
| 2722 | 2722 | ||
| 2723 | 2723 | /// Reject calls from safe code through unsafe function types. |
|
| 2724 | - | fn checkUnsafeCall(self: &mut Resolver, node: *ast::Node, info: *FnType) |
|
| 2724 | + | unsafe fn checkUnsafeCall(self: &mut Resolver, node: *ast::Node, info: *FnType) |
|
| 2725 | 2725 | throws (ResolveError) |
|
| 2726 | 2726 | { |
|
| 2727 | 2727 | if info.isUnsafe and not self.inUnsafeFn { |
|
| 2728 | 2728 | throw emitError(self, node, ErrorKind::UnsafeCall); |
|
| 2729 | 2729 | } |
| 2779 | 2779 | unsafe fn infer(self: &mut Resolver, node: *ast::Node) -> Type throws (ResolveError) { |
|
| 2780 | 2780 | return try visit(self, node, Type::Unknown); |
|
| 2781 | 2781 | } |
|
| 2782 | 2782 | ||
| 2783 | 2783 | /// Reject nested references while allowing a direct parameter reference. |
|
| 2784 | - | fn validateValueTypeReferences(self: &mut Resolver, node: *ast::Node, ty: Type) |
|
| 2784 | + | unsafe fn validateValueTypeReferences(self: &mut Resolver, node: *ast::Node, ty: Type) |
|
| 2785 | 2785 | throws (ResolveError) |
|
| 2786 | 2786 | { |
|
| 2787 | 2787 | if isRefType(ty) { |
|
| 2788 | 2788 | if let case Type::Pointer { target, .. } = ty { |
|
| 2789 | 2789 | if containsRef(*target) { |
| 2798 | 2798 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 2799 | 2799 | } |
|
| 2800 | 2800 | } |
|
| 2801 | 2801 | ||
| 2802 | 2802 | /// Require a type that may be stored or escape a call. |
|
| 2803 | - | fn ensureStorableType(self: &mut Resolver, node: *ast::Node, ty: Type) |
|
| 2803 | + | unsafe fn ensureStorableType(self: &mut Resolver, node: *ast::Node, ty: Type) |
|
| 2804 | 2804 | throws (ResolveError) |
|
| 2805 | 2805 | { |
|
| 2806 | 2806 | if containsRef(ty) { |
|
| 2807 | 2807 | throw emitError(self, node, ErrorKind::InvalidRefPosition); |
|
| 2808 | 2808 | } |
| 3022 | 3022 | } |
|
| 3023 | 3023 | return mask; |
|
| 3024 | 3024 | } |
|
| 3025 | 3025 | ||
| 3026 | 3026 | /// Ensure the `default` attribute is only applied to functions. |
|
| 3027 | - | fn ensureDefaultAttrNotAllowed(self: &mut Resolver, node: *ast::Node, attrs: u32) |
|
| 3027 | + | unsafe fn ensureDefaultAttrNotAllowed(self: &mut Resolver, node: *ast::Node, attrs: u32) |
|
| 3028 | 3028 | throws (ResolveError) |
|
| 3029 | 3029 | { |
|
| 3030 | 3030 | let defaultBit = ast::Attribute::Default as u32; |
|
| 3031 | 3031 | if (attrs & defaultBit) <> 0 { |
|
| 3032 | 3032 | throw emitError(self, node, ErrorKind::DefaultAttrOnlyOnFn); |
| 3286 | 3286 | try checkAssignable(self, arg, fieldType); |
|
| 3287 | 3287 | } |
|
| 3288 | 3288 | } |
|
| 3289 | 3289 | ||
| 3290 | 3290 | /// Check that the argument count of a constructor pattern or call matches the record field count. |
|
| 3291 | - | fn checkRecordArity(self: &mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) { |
|
| 3291 | + | unsafe fn checkRecordArity(self: &mut Resolver, args: *mut [*ast::Node], recInfo: RecordType, pattern: *ast::Node) throws (ResolveError) { |
|
| 3292 | 3292 | if args.len <> recInfo.fields.len { |
|
| 3293 | 3293 | throw emitError(self, pattern, ErrorKind::RecordFieldCountMismatch(CountMismatch { |
|
| 3294 | 3294 | expected: recInfo.fields.len as u32, |
|
| 3295 | 3295 | actual: args.len, |
|
| 3296 | 3296 | })); |
| 4074 | 4074 | ||
| 4075 | 4075 | /// Resolve a standalone method declaration (signature only). |
|
| 4076 | 4076 | /// Validates the receiver type and registers the method in the method table. |
|
| 4077 | 4077 | ||
| 4078 | 4078 | /// Extract the type name from a resolved receiver type node. |
|
| 4079 | - | fn receiverTypeName( |
|
| 4079 | + | unsafe fn receiverTypeName( |
|
| 4080 | 4080 | self: &mut Resolver, |
|
| 4081 | 4081 | receiverType: *ast::Node, |
|
| 4082 | 4082 | ) -> *[u8] throws (ResolveError) { |
|
| 4083 | 4083 | let case ast::NodeValue::TypeSig(ast::TypeSig::Pointer { valueType, .. }) = |
|
| 4084 | 4084 | receiverType.value |
| 5395 | 5395 | }; |
|
| 5396 | 5396 | let _ = try checkAssignable(self, args[1], Type::U32); |
|
| 5397 | 5397 | if args.len == 3 { |
|
| 5398 | 5398 | let _ = try checkAssignable(self, args[2], Type::U32); |
|
| 5399 | 5399 | } |
|
| 5400 | + | try requireUnsafe(self, node); |
|
| 5400 | 5401 | return setNodeType(self, node, Type::Slice { class, item: target, mutable }); |
|
| 5401 | 5402 | } |
|
| 5402 | 5403 | if args.len <> 1 { |
|
| 5403 | 5404 | throw emitError(self, node, ErrorKind::BuiltinArgCountMismatch(CountMismatch { |
|
| 5404 | 5405 | expected: 1, |
| 5700 | 5701 | try checkIndex(self, end); |
|
| 5701 | 5702 | } |
|
| 5702 | 5703 | } |
|
| 5703 | 5704 | ||
| 5704 | 5705 | /// Emit an error when a slice range with compile-tyime values exceeds the array length. |
|
| 5705 | - | fn validateArraySliceBounds(self: &mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) { |
|
| 5706 | + | unsafe fn validateArraySliceBounds(self: &mut Resolver, range: ast::Range, length: u32, site: *ast::Node) throws (ResolveError) { |
|
| 5706 | 5707 | let mut startVal: ?u32 = nil; |
|
| 5707 | 5708 | let mut endVal: ?u32 = length; |
|
| 5708 | 5709 | ||
| 5709 | 5710 | if let startNode = range.start { |
|
| 5710 | 5711 | if let val = constSliceIndex(self, startNode) { |
| 6057 | 6058 | }); |
|
| 6058 | 6059 | return setNodeType(self, node, arrayTy); |
|
| 6059 | 6060 | } |
|
| 6060 | 6061 | ||
| 6061 | 6062 | /// Resolve union variant access. |
|
| 6062 | - | fn resolveUnionVariantAccess( |
|
| 6063 | + | unsafe fn resolveUnionVariantAccess( |
|
| 6063 | 6064 | self: &mut Resolver, |
|
| 6064 | 6065 | node: *ast::Node, |
|
| 6065 | 6066 | access: ast::Access, |
|
| 6066 | 6067 | unionType: UnionType, |
|
| 6067 | 6068 | variantName: *[u8] |
| 6350 | 6351 | } |
|
| 6351 | 6352 | return types::PointerClass::Ref; |
|
| 6352 | 6353 | } |
|
| 6353 | 6354 | ||
| 6354 | 6355 | /// Select an address type without extending the target storage lifetime. |
|
| 6355 | - | fn addressClass(self: &mut Resolver, target: *ast::Node, hint: Type) -> types::PointerClass |
|
| 6356 | + | unsafe fn addressClass(self: &mut Resolver, target: *ast::Node, hint: Type) -> types::PointerClass |
|
| 6356 | 6357 | throws (ResolveError) |
|
| 6357 | 6358 | { |
|
| 6358 | 6359 | if isUnsafePointerType(hint) { |
|
| 6359 | 6360 | try requireUnsafe(self, target); |
|
| 6360 | 6361 | return types::PointerClass::Unsafe; |
lib/std/lang/resolver/tests.rad
+25 -5
| 4135 | 4135 | /// Test @sliceOf with correct arguments succeeds. |
|
| 4136 | 4136 | @test unsafe fn testResolveSliceOfCorrect() throws (testing::TestError) { |
|
| 4137 | 4137 | // Immutable pointer. |
|
| 4138 | 4138 | { |
|
| 4139 | 4139 | let mut a = testResolver(); |
|
| 4140 | - | let program = "fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 4140 | + | let program = "unsafe fn f(ptr: *u8, len: u32) -> *[u8] { return @sliceOf(ptr, len); }"; |
|
| 4141 | 4141 | let result = try resolveProgramStr(&mut a, program); |
|
| 4142 | 4142 | try expectNoErrors(&result); |
|
| 4143 | 4143 | } |
|
| 4144 | 4144 | // Mutable pointer produces mutable slice. |
|
| 4145 | 4145 | { |
|
| 4146 | 4146 | let mut a = testResolver(); |
|
| 4147 | - | let program = "fn f(ptr: *mut u8, len: u32) -> *mut [u8] { return @sliceOf(ptr, len); }"; |
|
| 4147 | + | let program = "unsafe fn f(ptr: *mut u8, len: u32) -> *mut [u8] { return @sliceOf(ptr, len); }"; |
|
| 4148 | 4148 | let result = try resolveProgramStr(&mut a, program); |
|
| 4149 | 4149 | try expectNoErrors(&result); |
|
| 4150 | 4150 | } |
|
| 4151 | 4151 | } |
|
| 4152 | 4152 |
| 4229 | 4229 | ||
| 4230 | 4230 | /// Test @sliceOf with 3 arguments (ptr, len, cap) succeeds. |
|
| 4231 | 4231 | @test unsafe fn testResolveSliceOfWithCap() throws (testing::TestError) { |
|
| 4232 | 4232 | { |
|
| 4233 | 4233 | let mut a = testResolver(); |
|
| 4234 | - | let program = "fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4234 | + | let program = "unsafe fn f(ptr: *u8, len: u32, cap: u32) -> *[u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4235 | 4235 | let result = try resolveProgramStr(&mut a, program); |
|
| 4236 | 4236 | try expectNoErrors(&result); |
|
| 4237 | 4237 | } |
|
| 4238 | 4238 | // Mutable pointer produces mutable slice. |
|
| 4239 | 4239 | { |
|
| 4240 | 4240 | let mut a = testResolver(); |
|
| 4241 | - | let program = "fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4241 | + | let program = "unsafe fn f(ptr: *mut u8, len: u32, cap: u32) -> *mut [u8] { return @sliceOf(ptr, len, cap); }"; |
|
| 4242 | 4242 | let result = try resolveProgramStr(&mut a, program); |
|
| 4243 | 4243 | try expectNoErrors(&result); |
|
| 4244 | 4244 | } |
|
| 4245 | 4245 | } |
|
| 4246 | 4246 |
| 5358 | 5358 | try expectAnalyzeOk("fn run(value: *u32) -> *u32 { return &*value; }"); |
|
| 5359 | 5359 | try expectAnalyzeOk("fn run(values: *[u32]) -> *[u32] { return &values[..]; }"); |
|
| 5360 | 5360 | try expectAnalyzeOk("record Cell { value: u32 } fn run(value: *Cell) -> *u32 { return &value.value; }"); |
|
| 5361 | 5361 | try expectAnalyzeOk("fn run(values: *[u32]) -> *u32 { return &values[0]; }"); |
|
| 5362 | 5362 | try expectAnalyzeOk("fn run(values: *[u32]) -> *u32 { return values.ptr; }"); |
|
| 5363 | - | try expectAnalyzeOk("fn run(value: *u32) -> *[u32] { return @sliceOf(value, 1); }"); |
|
| 5363 | + | try expectAnalyzeOk("unsafe fn run(value: *u32) -> *[u32] { return @sliceOf(value, 1); }"); |
|
| 5364 | 5364 | } |
|
| 5365 | 5365 | ||
| 5366 | 5366 | /// References are rejected from every nested or storable type position. |
|
| 5367 | 5367 | @test unsafe fn testNestedRefPositionsRejected() throws (testing::TestError) { |
|
| 5368 | 5368 | { |
| 5508 | 5508 | try expectAnalyzeOk("fn run(p: *u8) -> *opaque { return p as *opaque; }"); |
|
| 5509 | 5509 | try expectAnalyzeOk("fn run(s: *[u8]) -> *[opaque] { return s as *[opaque]; }"); |
|
| 5510 | 5510 | try expectAnalyzeOk("fn run(p: *mut u8) -> *u8 { return p as *u8; }"); |
|
| 5511 | 5511 | } |
|
| 5512 | 5512 | ||
| 5513 | + | /// Explicit slice bounds require an unsafe function. |
|
| 5514 | + | @test unsafe fn testSliceConstructionRequiresUnsafe() throws (testing::TestError) { |
|
| 5515 | + | let programs = &[ |
|
| 5516 | + | "fn run(p: *u8) -> *[u8] { return @sliceOf(p, 100); }", |
|
| 5517 | + | "fn run(p: *mut u8) -> *mut [u8] { return @sliceOf(p, 0, 100); }", |
|
| 5518 | + | "fn run(p: &u8) -> u8 { return @sliceOf(p, 100)[99]; }", |
|
| 5519 | + | "fn run(p: &mut u8) { set @sliceOf(p, 100)[99] = 1; }", |
|
| 5520 | + | "static DATA: [u8; 1] = [42]; fn run() -> u8 { let s = @sliceOf(&DATA[0], 100); return s[99]; }", |
|
| 5521 | + | ]; |
|
| 5522 | + | for program in programs { |
|
| 5523 | + | let mut a = testResolver(); |
|
| 5524 | + | let result = try resolveProgramStr(&mut a, program); |
|
| 5525 | + | try expectErrorKind(&result, super::ErrorKind::UnsafeOperation); |
|
| 5526 | + | } |
|
| 5527 | + | try expectAnalyzeOk("unsafe fn run(p: *u8) -> *[u8] { return @sliceOf(p, 100); }"); |
|
| 5528 | + | try expectAnalyzeOk("unsafe fn run(p: *mut u8) -> *mut [u8] { return @sliceOf(p, 0, 100); }"); |
|
| 5529 | + | try expectAnalyzeOk("unsafe fn run(p: &u8) -> u8 { return @sliceOf(p, 1)[0]; }"); |
|
| 5530 | + | try expectAnalyzeOk("fn run(s: *[u8]) -> *[u8] { return &s[..]; }"); |
|
| 5531 | + | } |
|
| 5532 | + | ||
| 5513 | 5533 | /// Unsafe declarations may compose unsafe operations and calls. |
|
| 5514 | 5534 | @test unsafe fn testUnsafePointerOperationsAllowed() throws (testing::TestError) { |
|
| 5515 | 5535 | 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); }"; |
|
| 5516 | 5536 | try expectAnalyzeOk(program); |
|
| 5517 | 5537 | } |
test/tests/slice.basic.rad
+2 -2
| 18 | 18 | let c: *[i32] = s; |
|
| 19 | 19 | return c; |
|
| 20 | 20 | } |
|
| 21 | 21 | ||
| 22 | 22 | /// Builds a slice header from a pointer and length. |
|
| 23 | - | fn sliceOf(ptr: *i32, len: u32) -> *[i32] { |
|
| 23 | + | unsafe fn sliceOf(ptr: *i32, len: u32) -> *[i32] { |
|
| 24 | 24 | return @sliceOf(ptr, len); |
|
| 25 | 25 | } |
|
| 26 | 26 | ||
| 27 | 27 | /// Builds a slice header from pointer, length, and capacity. |
|
| 28 | - | fn sliceOfWithCap(ptr: *i32, len: u32, cap: u32) -> *[i32] { |
|
| 28 | + | unsafe fn sliceOfWithCap(ptr: *i32, len: u32, cap: u32) -> *[i32] { |
|
| 29 | 29 | return @sliceOf(ptr, len, cap); |
|
| 30 | 30 | } |
test/tests/slice.construct.unsafe.rad
added
+15 -0
| 1 | + | //! returns: 0 |
|
| 2 | + | ||
| 3 | + | /// Backing storage for explicit slice bounds. |
|
| 4 | + | static DATA: [u8; 3] = [11, 22, 33]; |
|
| 5 | + | ||
| 6 | + | /// Construct valid slices from stored pointers in an unsafe function. |
|
| 7 | + | @default unsafe fn main() -> i32 { |
|
| 8 | + | let s = @sliceOf(&DATA[0], 3); |
|
| 9 | + | if s[2] <> 33 { return 1; } |
|
| 10 | + | let t = @sliceOf(&mut DATA[0], 2, 3); |
|
| 11 | + | if t.len <> 2 or t.cap <> 3 { return 2; } |
|
| 12 | + | set t[1] = 44; |
|
| 13 | + | if DATA[1] <> 44 { return 3; } |
|
| 14 | + | return 0; |
|
| 15 | + | } |
test/tests/slice.of.rad
+2 -1
| 1 | 1 | //! returns: 0 |
|
| 2 | 2 | //! Test that @sliceOf produces a fat pointer with the correct length. |
|
| 3 | 3 | ||
| 4 | - | fn makeSlice(ptr: *unsafe mut i32, count: u32) -> *unsafe mut [i32] { |
|
| 4 | + | /// Build a slice over the caller's initialized storage. |
|
| 5 | + | unsafe fn makeSlice(ptr: *unsafe mut i32, count: u32) -> *unsafe mut [i32] { |
|
| 5 | 6 | return @sliceOf(ptr, count); |
|
| 6 | 7 | } |
|
| 7 | 8 | ||
| 8 | 9 | @default unsafe fn main() -> i32 { |
|
| 9 | 10 | let mut arr: [i32; 4] = [10, 20, 30, 40]; |