| author | |
| committer | |
| log | bc95c63cf227226861d7fbf63fa6c779fed8abf8 |
| tree | 1e004f521047ead2fb52855da7bc4fa819020548 |
| parent | 51cbd968203f348051b8c2bdc005ca5294a79ceb |
| parent | 290dc5d95b986464a5be91bb3fd0ada2dd0840ae |
| signature |
fix std.mem.addNullByte and implement sentinel slicing17 files changed, 334 insertions(+), 65 deletions(-)
lib/std/buffer.zig+2-2| ... | ... | @@ -82,11 +82,11 @@ pub const Buffer = struct { |
| 82 | 82 | } |
| 83 | 83 | |
| 84 | 84 | pub fn toSlice(self: Buffer) [:0]u8 { |
| 85 | return self.list.toSlice()[0..self.len()]; | |
| 85 | return self.list.toSlice()[0..self.len() :0]; | |
| 86 | 86 | } |
| 87 | 87 | |
| 88 | 88 | pub fn toSliceConst(self: Buffer) [:0]const u8 { |
| 89 | return self.list.toSliceConst()[0..self.len()]; | |
| 89 | return self.list.toSliceConst()[0..self.len() :0]; | |
| 90 | 90 | } |
| 91 | 91 | |
| 92 | 92 | pub fn shrink(self: *Buffer, new_len: usize) void { |
lib/std/cstr.zig+10-2| ... | ... | @@ -31,13 +31,21 @@ fn testCStrFnsImpl() void { |
| 31 | 31 | testing.expect(mem.len(u8, "123456789") == 9); |
| 32 | 32 | } |
| 33 | 33 | |
| 34 | /// Returns a mutable slice with 1 more byte of length which is a null byte. | |
| 34 | /// Returns a mutable, null-terminated slice with the same length as `slice`. | |
| 35 | 35 | /// Caller owns the returned memory. |
| 36 | 36 | pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![:0]u8 { |
| 37 | 37 | const result = try allocator.alloc(u8, slice.len + 1); |
| 38 | 38 | mem.copy(u8, result, slice); |
| 39 | 39 | result[slice.len] = 0; |
| 40 | return result; | |
| 40 | return result[0..slice.len :0]; | |
| 41 | } | |
| 42 | ||
| 43 | test "addNullByte" { | |
| 44 | var buf: [30]u8 = undefined; | |
| 45 | const allocator = &std.heap.FixedBufferAllocator.init(&buf).allocator; | |
| 46 | const slice = try addNullByte(allocator, "hello"[0..4]); | |
| 47 | testing.expect(slice.len == 4); | |
| 48 | testing.expect(slice[4] == 0); | |
| 41 | 49 | } |
| 42 | 50 | |
| 43 | 51 | pub const NullTerminated2DArray = struct { |
lib/std/debug.zig+19-15| ... | ... | @@ -219,7 +219,7 @@ pub fn panic(comptime format: []const u8, args: var) noreturn { |
| 219 | 219 | } |
| 220 | 220 | |
| 221 | 221 | /// TODO multithreaded awareness |
| 222 | var panicking: u8 = 0; // TODO make this a bool | |
| 222 | var panicking: u8 = 0; | |
| 223 | 223 | |
| 224 | 224 | pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn { |
| 225 | 225 | @setCold(true); |
| ... | ... | @@ -230,21 +230,25 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c |
| 230 | 230 | resetSegfaultHandler(); |
| 231 | 231 | } |
| 232 | 232 | |
| 233 | if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) { | |
| 234 | // Panicked during a panic. | |
| 235 | ||
| 236 | // TODO detect if a different thread caused the panic, because in that case | |
| 237 | // we would want to return here instead of calling abort, so that the thread | |
| 238 | // which first called panic can finish printing a stack trace. | |
| 239 | os.abort(); | |
| 240 | } | |
| 241 | const stderr = getStderrStream(); | |
| 242 | stderr.print(format ++ "\n", args) catch os.abort(); | |
| 243 | if (trace) |t| { | |
| 244 | dumpStackTrace(t.*); | |
| 233 | switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) { | |
| 234 | 0 => { | |
| 235 | const stderr = getStderrStream(); | |
| 236 | stderr.print(format ++ "\n", args) catch os.abort(); | |
| 237 | if (trace) |t| { | |
| 238 | dumpStackTrace(t.*); | |
| 239 | } | |
| 240 | dumpCurrentStackTrace(first_trace_addr); | |
| 241 | }, | |
| 242 | 1 => { | |
| 243 | // TODO detect if a different thread caused the panic, because in that case | |
| 244 | // we would want to return here instead of calling abort, so that the thread | |
| 245 | // which first called panic can finish printing a stack trace. | |
| 246 | warn("Panicked during a panic. Aborting.\n", .{}); | |
| 247 | }, | |
| 248 | else => { | |
| 249 | // Panicked while printing "Panicked during a panic." | |
| 250 | }, | |
| 245 | 251 | } |
| 246 | dumpCurrentStackTrace(first_trace_addr); | |
| 247 | ||
| 248 | 252 | os.abort(); |
| 249 | 253 | } |
| 250 | 254 |
lib/std/fs.zig+4-5| ... | ... | @@ -221,16 +221,16 @@ pub const AtomicFile = struct { |
| 221 | 221 | } |
| 222 | 222 | |
| 223 | 223 | tmp_path_buf[tmp_path_len] = 0; |
| 224 | const tmp_path_slice = tmp_path_buf[0..tmp_path_len :0]; | |
| 224 | 225 | |
| 225 | 226 | const my_cwd = cwd(); |
| 226 | 227 | |
| 227 | 228 | while (true) { |
| 228 | 229 | try crypto.randomBytes(rand_buf[0..]); |
| 229 | b64_fs_encoder.encode(tmp_path_buf[dirname_component_len..tmp_path_len], &rand_buf); | |
| 230 | b64_fs_encoder.encode(tmp_path_slice[dirname_component_len..tmp_path_len], &rand_buf); | |
| 230 | 231 | |
| 231 | // TODO https://github.com/ziglang/zig/issues/3770 to clean up this @ptrCast | |
| 232 | 232 | const file = my_cwd.createFileC( |
| 233 | @ptrCast([*:0]u8, &tmp_path_buf), | |
| 233 | tmp_path_slice, | |
| 234 | 234 | .{ .mode = mode, .exclusive = true }, |
| 235 | 235 | ) catch |err| switch (err) { |
| 236 | 236 | error.PathAlreadyExists => continue, |
| ... | ... | @@ -1488,8 +1488,7 @@ pub fn openSelfExe() OpenSelfExeError!File { |
| 1488 | 1488 | var buf: [MAX_PATH_BYTES]u8 = undefined; |
| 1489 | 1489 | const self_exe_path = try selfExePath(&buf); |
| 1490 | 1490 | buf[self_exe_path.len] = 0; |
| 1491 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 | |
| 1492 | return openFileAbsoluteC(@ptrCast([*:0]u8, self_exe_path.ptr), .{}); | |
| 1491 | return openFileAbsoluteC(self_exe_path[0..self_exe_path.len :0].ptr, .{}); | |
| 1493 | 1492 | } |
| 1494 | 1493 | |
| 1495 | 1494 | test "openSelfExe" { |
lib/std/mem.zig+5-4| ... | ... | @@ -231,9 +231,10 @@ pub const Allocator = struct { |
| 231 | 231 | pub fn free(self: *Allocator, memory: var) void { |
| 232 | 232 | const Slice = @typeInfo(@TypeOf(memory)).Pointer; |
| 233 | 233 | const bytes = @sliceToBytes(memory); |
| 234 | if (bytes.len == 0) return; | |
| 234 | const bytes_len = bytes.len + @boolToInt(Slice.sentinel != null); | |
| 235 | if (bytes_len == 0) return; | |
| 235 | 236 | const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr)); |
| 236 | const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes.len], Slice.alignment, 0, 1); | |
| 237 | const shrink_result = self.shrinkFn(self, non_const_ptr[0..bytes_len], Slice.alignment, 0, 1); | |
| 237 | 238 | assert(shrink_result.len == 0); |
| 238 | 239 | } |
| 239 | 240 | }; |
| ... | ... | @@ -363,11 +364,11 @@ pub fn len(comptime T: type, ptr: [*:0]const T) usize { |
| 363 | 364 | } |
| 364 | 365 | |
| 365 | 366 | pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T { |
| 366 | return ptr[0..len(T, ptr)]; | |
| 367 | return ptr[0..len(T, ptr) :0]; | |
| 367 | 368 | } |
| 368 | 369 | |
| 369 | 370 | pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T { |
| 370 | return ptr[0..len(T, ptr)]; | |
| 371 | return ptr[0..len(T, ptr) :0]; | |
| 371 | 372 | } |
| 372 | 373 | |
| 373 | 374 | /// Returns true if all elements in a slice are equal to the scalar value provided |
lib/std/os.zig+9-16| ... | ... | @@ -805,9 +805,9 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e |
| 805 | 805 | mem.copy(u8, &path_buf, search_path); |
| 806 | 806 | path_buf[search_path.len] = '/'; |
| 807 | 807 | mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice); |
| 808 | path_buf[search_path.len + file_slice.len + 1] = 0; | |
| 809 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 | |
| 810 | err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp); | |
| 808 | const path_len = search_path.len + file_slice.len + 1; | |
| 809 | path_buf[path_len] = 0; | |
| 810 | err = execveC(path_buf[0..path_len :0].ptr, child_argv, envp); | |
| 811 | 811 | switch (err) { |
| 812 | 812 | error.AccessDenied => seen_eacces = true, |
| 813 | 813 | error.FileNotFound, error.NotDir => {}, |
| ... | ... | @@ -841,18 +841,14 @@ pub fn execvpe( |
| 841 | 841 | const arg_buf = try allocator.alloc(u8, arg.len + 1); |
| 842 | 842 | @memcpy(arg_buf.ptr, arg.ptr, arg.len); |
| 843 | 843 | arg_buf[arg.len] = 0; |
| 844 | ||
| 845 | // TODO avoid @ptrCast using slice syntax with https://github.com/ziglang/zig/issues/3770 | |
| 846 | argv_buf[i] = @ptrCast([*:0]u8, arg_buf.ptr); | |
| 844 | argv_buf[i] = arg_buf[0..arg.len :0].ptr; | |
| 847 | 845 | } |
| 848 | 846 | argv_buf[argv_slice.len] = null; |
| 847 | const argv_ptr = argv_buf[0..argv_slice.len :null].ptr; | |
| 849 | 848 | |
| 850 | 849 | const envp_buf = try createNullDelimitedEnvMap(allocator, env_map); |
| 851 | 850 | defer freeNullDelimitedEnvMap(allocator, envp_buf); |
| 852 | 851 | |
| 853 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 | |
| 854 | const argv_ptr = @ptrCast([*:null]?[*:0]u8, argv_buf.ptr); | |
| 855 | ||
| 856 | 852 | return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr); |
| 857 | 853 | } |
| 858 | 854 | |
| ... | ... | @@ -869,16 +865,13 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std. |
| 869 | 865 | @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len); |
| 870 | 866 | env_buf[pair.key.len] = '='; |
| 871 | 867 | @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len); |
| 872 | env_buf[env_buf.len - 1] = 0; | |
| 873 | ||
| 874 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 | |
| 875 | envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr); | |
| 868 | const len = env_buf.len - 1; | |
| 869 | env_buf[len] = 0; | |
| 870 | envp_buf[i] = env_buf[0..len :0].ptr; | |
| 876 | 871 | } |
| 877 | 872 | assert(i == envp_count); |
| 878 | 873 | } |
| 879 | // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770 | |
| 880 | assert(envp_buf[envp_count] == null); | |
| 881 | return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count]; | |
| 874 | return envp_buf[0..envp_count :null]; | |
| 882 | 875 | } |
| 883 | 876 | |
| 884 | 877 | pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void { |
lib/std/zig/ast.zig+5| ... | ... | @@ -1701,6 +1701,7 @@ pub const Node = struct { |
| 1701 | 1701 | pub const Slice = struct { |
| 1702 | 1702 | start: *Node, |
| 1703 | 1703 | end: ?*Node, |
| 1704 | sentinel: ?*Node, | |
| 1704 | 1705 | }; |
| 1705 | 1706 | }; |
| 1706 | 1707 | |
| ... | ... | @@ -1732,6 +1733,10 @@ pub const Node = struct { |
| 1732 | 1733 | if (i < 1) return end; |
| 1733 | 1734 | i -= 1; |
| 1734 | 1735 | } |
| 1736 | if (range.sentinel) |sentinel| { | |
| 1737 | if (i < 1) return sentinel; | |
| 1738 | i -= 1; | |
| 1739 | } | |
| 1735 | 1740 | }, |
| 1736 | 1741 | .ArrayInitializer => |*exprs| { |
| 1737 | 1742 | if (i < exprs.len) return exprs.at(i).*; |
lib/std/zig/parse.zig+6-1| ... | ... | @@ -2331,7 +2331,7 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node |
| 2331 | 2331 | } |
| 2332 | 2332 | |
| 2333 | 2333 | /// SuffixOp |
| 2334 | /// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET | |
| 2334 | /// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET | |
| 2335 | 2335 | /// / DOT IDENTIFIER |
| 2336 | 2336 | /// / DOTASTERISK |
| 2337 | 2337 | /// / DOTQUESTIONMARK |
| ... | ... | @@ -2349,11 +2349,16 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node { |
| 2349 | 2349 | |
| 2350 | 2350 | if (eatToken(it, .Ellipsis2) != null) { |
| 2351 | 2351 | const end_expr = try parseExpr(arena, it, tree); |
| 2352 | const sentinel: ?*ast.Node = if (eatToken(it, .Colon) != null) | |
| 2353 | try parseExpr(arena, it, tree) | |
| 2354 | else | |
| 2355 | null; | |
| 2352 | 2356 | break :blk OpAndToken{ |
| 2353 | 2357 | .op = Op{ |
| 2354 | 2358 | .Slice = Op.Slice{ |
| 2355 | 2359 | .start = index_expr, |
| 2356 | 2360 | .end = end_expr, |
| 2361 | .sentinel = sentinel, | |
| 2357 | 2362 | }, |
| 2358 | 2363 | }, |
| 2359 | 2364 | .token = try expectToken(it, tree, .RBracket), |
lib/std/zig/parser_test.zig+3| ... | ... | @@ -419,10 +419,13 @@ test "zig fmt: pointer of unknown length" { |
| 419 | 419 | test "zig fmt: spaces around slice operator" { |
| 420 | 420 | try testCanonical( |
| 421 | 421 | \\var a = b[c..d]; |
| 422 | \\var a = b[c..d :0]; | |
| 422 | 423 | \\var a = b[c + 1 .. d]; |
| 423 | 424 | \\var a = b[c + 1 ..]; |
| 424 | 425 | \\var a = b[c .. d + 1]; |
| 426 | \\var a = b[c .. d + 1 :0]; | |
| 425 | 427 | \\var a = b[c.a..d.e]; |
| 428 | \\var a = b[c.a..d.e :0]; | |
| 426 | 429 | \\ |
| 427 | 430 | ); |
| 428 | 431 | } |
lib/std/zig/render.zig+7-1| ... | ... | @@ -689,7 +689,13 @@ fn renderExpression( |
| 689 | 689 | try renderExpression(allocator, stream, tree, indent, start_col, range.start, after_start_space); |
| 690 | 690 | try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // .. |
| 691 | 691 | if (range.end) |end| { |
| 692 | try renderExpression(allocator, stream, tree, indent, start_col, end, Space.None); | |
| 692 | const after_end_space = if (range.sentinel != null) Space.Space else Space.None; | |
| 693 | try renderExpression(allocator, stream, tree, indent, start_col, end, after_end_space); | |
| 694 | } | |
| 695 | if (range.sentinel) |sentinel| { | |
| 696 | const colon = tree.prevToken(sentinel.firstToken()); | |
| 697 | try renderToken(tree, stream, colon, indent, start_col, Space.None); // : | |
| 698 | try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); | |
| 693 | 699 | } |
| 694 | 700 | return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ] |
| 695 | 701 | }, |
src/all_types.hpp+3| ... | ... | @@ -810,6 +810,7 @@ struct AstNodeSliceExpr { |
| 810 | 810 | AstNode *array_ref_expr; |
| 811 | 811 | AstNode *start; |
| 812 | 812 | AstNode *end; |
| 813 | AstNode *sentinel; // can be null | |
| 813 | 814 | }; |
| 814 | 815 | |
| 815 | 816 | struct AstNodeFieldAccessExpr { |
| ... | ... | @@ -1778,6 +1779,7 @@ enum PanicMsgId { |
| 1778 | 1779 | PanicMsgIdResumedFnPendingAwait, |
| 1779 | 1780 | PanicMsgIdBadNoAsyncCall, |
| 1780 | 1781 | PanicMsgIdResumeNotSuspendedFn, |
| 1782 | PanicMsgIdBadSentinel, | |
| 1781 | 1783 | |
| 1782 | 1784 | PanicMsgIdCount, |
| 1783 | 1785 | }; |
| ... | ... | @@ -3388,6 +3390,7 @@ struct IrInstructionSliceSrc { |
| 3388 | 3390 | IrInstruction *ptr; |
| 3389 | 3391 | IrInstruction *start; |
| 3390 | 3392 | IrInstruction *end; |
| 3393 | IrInstruction *sentinel; | |
| 3391 | 3394 | ResultLoc *result_loc; |
| 3392 | 3395 | }; |
| 3393 | 3396 |
src/codegen.cpp+48-3| ... | ... | @@ -941,6 +941,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) { |
| 941 | 941 | return buf_create_from_str("async function called with noasync suspended"); |
| 942 | 942 | case PanicMsgIdResumeNotSuspendedFn: |
| 943 | 943 | return buf_create_from_str("resumed a non-suspended function"); |
| 944 | case PanicMsgIdBadSentinel: | |
| 945 | return buf_create_from_str("sentinel mismatch"); | |
| 944 | 946 | } |
| 945 | 947 | zig_unreachable(); |
| 946 | 948 | } |
| ... | ... | @@ -1419,6 +1421,27 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val, |
| 1419 | 1421 | LLVMPositionBuilderAtEnd(g->builder, ok_block); |
| 1420 | 1422 | } |
| 1421 | 1423 | |
| 1424 | static void add_sentinel_check(CodeGen *g, LLVMValueRef sentinel_elem_ptr, ZigValue *sentinel) { | |
| 1425 | LLVMValueRef expected_sentinel = gen_const_val(g, sentinel, ""); | |
| 1426 | ||
| 1427 | LLVMValueRef actual_sentinel = gen_load_untyped(g, sentinel_elem_ptr, 0, false, ""); | |
| 1428 | LLVMValueRef ok_bit; | |
| 1429 | if (sentinel->type->id == ZigTypeIdFloat) { | |
| 1430 | ok_bit = LLVMBuildFCmp(g->builder, LLVMRealOEQ, actual_sentinel, expected_sentinel, ""); | |
| 1431 | } else { | |
| 1432 | ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, actual_sentinel, expected_sentinel, ""); | |
| 1433 | } | |
| 1434 | ||
| 1435 | LLVMBasicBlockRef fail_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelFail"); | |
| 1436 | LLVMBasicBlockRef ok_block = LLVMAppendBasicBlock(g->cur_fn_val, "SentinelOk"); | |
| 1437 | LLVMBuildCondBr(g->builder, ok_bit, ok_block, fail_block); | |
| 1438 | ||
| 1439 | LLVMPositionBuilderAtEnd(g->builder, fail_block); | |
| 1440 | gen_safety_crash(g, PanicMsgIdBadSentinel); | |
| 1441 | ||
| 1442 | LLVMPositionBuilderAtEnd(g->builder, ok_block); | |
| 1443 | } | |
| 1444 | ||
| 1422 | 1445 | static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) { |
| 1423 | 1446 | LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type)); |
| 1424 | 1447 | LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, ""); |
| ... | ... | @@ -5244,6 +5267,9 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 5244 | 5267 | |
| 5245 | 5268 | bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base); |
| 5246 | 5269 | |
| 5270 | ZigType *res_slice_ptr_type = instruction->base.value->type->data.structure.fields[slice_ptr_index]->type_entry; | |
| 5271 | ZigValue *sentinel = res_slice_ptr_type->data.pointer.sentinel; | |
| 5272 | ||
| 5247 | 5273 | if (array_type->id == ZigTypeIdArray || |
| 5248 | 5274 | (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle)) |
| 5249 | 5275 | { |
| ... | ... | @@ -5265,6 +5291,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 5265 | 5291 | LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type, |
| 5266 | 5292 | array_type->data.array.len, false); |
| 5267 | 5293 | add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end); |
| 5294 | ||
| 5295 | if (sentinel != nullptr) { | |
| 5296 | LLVMValueRef indices[] = { | |
| 5297 | LLVMConstNull(g->builtin_types.entry_usize->llvm_type), | |
| 5298 | end_val, | |
| 5299 | }; | |
| 5300 | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, indices, 2, ""); | |
| 5301 | add_sentinel_check(g, sentinel_elem_ptr, sentinel); | |
| 5302 | } | |
| 5268 | 5303 | } |
| 5269 | 5304 | } |
| 5270 | 5305 | if (!type_has_bits(array_type)) { |
| ... | ... | @@ -5297,6 +5332,10 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 5297 | 5332 | |
| 5298 | 5333 | if (want_runtime_safety) { |
| 5299 | 5334 | add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 5335 | if (sentinel != nullptr) { | |
| 5336 | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, array_ptr, &end_val, 1, ""); | |
| 5337 | add_sentinel_check(g, sentinel_elem_ptr, sentinel); | |
| 5338 | } | |
| 5300 | 5339 | } |
| 5301 | 5340 | |
| 5302 | 5341 | if (type_has_bits(array_type)) { |
| ... | ... | @@ -5337,18 +5376,24 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst |
| 5337 | 5376 | end_val = prev_end; |
| 5338 | 5377 | } |
| 5339 | 5378 | |
| 5379 | LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); | |
| 5380 | LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); | |
| 5381 | ||
| 5340 | 5382 | if (want_runtime_safety) { |
| 5341 | 5383 | assert(prev_end); |
| 5342 | 5384 | add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val); |
| 5343 | 5385 | if (instruction->end) { |
| 5344 | 5386 | add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end); |
| 5387 | ||
| 5388 | if (sentinel != nullptr) { | |
| 5389 | LLVMValueRef sentinel_elem_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &end_val, 1, ""); | |
| 5390 | add_sentinel_check(g, sentinel_elem_ptr, sentinel); | |
| 5391 | } | |
| 5345 | 5392 | } |
| 5346 | 5393 | } |
| 5347 | 5394 | |
| 5348 | LLVMValueRef src_ptr_ptr = LLVMBuildStructGEP(g->builder, array_ptr, (unsigned)ptr_index, ""); | |
| 5349 | LLVMValueRef src_ptr = gen_load_untyped(g, src_ptr_ptr, 0, false, ""); | |
| 5350 | 5395 | LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, ""); |
| 5351 | LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, (unsigned)len_index, ""); | |
| 5396 | LLVMValueRef slice_start_ptr = LLVMBuildInBoundsGEP(g->builder, src_ptr, &start_val, 1, ""); | |
| 5352 | 5397 | gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false); |
| 5353 | 5398 | |
| 5354 | 5399 | LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, ""); |
src/ir.cpp+100-12| ... | ... | @@ -2967,18 +2967,21 @@ static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *sou |
| 2967 | 2967 | } |
| 2968 | 2968 | |
| 2969 | 2969 | static IrInstruction *ir_build_slice_src(IrBuilder *irb, Scope *scope, AstNode *source_node, |
| 2970 | IrInstruction *ptr, IrInstruction *start, IrInstruction *end, bool safety_check_on, ResultLoc *result_loc) | |
| 2970 | IrInstruction *ptr, IrInstruction *start, IrInstruction *end, IrInstruction *sentinel, | |
| 2971 | bool safety_check_on, ResultLoc *result_loc) | |
| 2971 | 2972 | { |
| 2972 | 2973 | IrInstructionSliceSrc *instruction = ir_build_instruction<IrInstructionSliceSrc>(irb, scope, source_node); |
| 2973 | 2974 | instruction->ptr = ptr; |
| 2974 | 2975 | instruction->start = start; |
| 2975 | 2976 | instruction->end = end; |
| 2977 | instruction->sentinel = sentinel; | |
| 2976 | 2978 | instruction->safety_check_on = safety_check_on; |
| 2977 | 2979 | instruction->result_loc = result_loc; |
| 2978 | 2980 | |
| 2979 | 2981 | ir_ref_instruction(ptr, irb->current_basic_block); |
| 2980 | 2982 | ir_ref_instruction(start, irb->current_basic_block); |
| 2981 | 2983 | if (end) ir_ref_instruction(end, irb->current_basic_block); |
| 2984 | if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block); | |
| 2982 | 2985 | |
| 2983 | 2986 | return &instruction->base; |
| 2984 | 2987 | } |
| ... | ... | @@ -8483,6 +8486,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, |
| 8483 | 8486 | AstNode *array_node = slice_expr->array_ref_expr; |
| 8484 | 8487 | AstNode *start_node = slice_expr->start; |
| 8485 | 8488 | AstNode *end_node = slice_expr->end; |
| 8489 | AstNode *sentinel_node = slice_expr->sentinel; | |
| 8486 | 8490 | |
| 8487 | 8491 | IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr); |
| 8488 | 8492 | if (ptr_value == irb->codegen->invalid_instruction) |
| ... | ... | @@ -8501,7 +8505,17 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node, |
| 8501 | 8505 | end_value = nullptr; |
| 8502 | 8506 | } |
| 8503 | 8507 | |
| 8504 | IrInstruction *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value, true, result_loc); | |
| 8508 | IrInstruction *sentinel_value; | |
| 8509 | if (sentinel_node) { | |
| 8510 | sentinel_value = ir_gen_node(irb, sentinel_node, scope); | |
| 8511 | if (sentinel_value == irb->codegen->invalid_instruction) | |
| 8512 | return irb->codegen->invalid_instruction; | |
| 8513 | } else { | |
| 8514 | sentinel_value = nullptr; | |
| 8515 | } | |
| 8516 | ||
| 8517 | IrInstruction *slice = ir_build_slice_src(irb, scope, node, ptr_value, start_value, end_value, | |
| 8518 | sentinel_value, true, result_loc); | |
| 8505 | 8519 | return ir_lval_wrap(irb, scope, slice, lval, result_loc); |
| 8506 | 8520 | } |
| 8507 | 8521 | |
| ... | ... | @@ -10533,6 +10547,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted |
| 10533 | 10547 | result.id = ConstCastResultIdInvalid; |
| 10534 | 10548 | return result; |
| 10535 | 10549 | } |
| 10550 | bool ok_sentinels = | |
| 10551 | wanted_ptr_type->data.pointer.sentinel == nullptr || | |
| 10552 | (actual_ptr_type->data.pointer.sentinel != nullptr && | |
| 10553 | const_values_equal(ira->codegen, wanted_ptr_type->data.pointer.sentinel, | |
| 10554 | actual_ptr_type->data.pointer.sentinel)); | |
| 10555 | if (!ok_sentinels) { | |
| 10556 | result.id = ConstCastResultIdPtrSentinel; | |
| 10557 | result.data.bad_ptr_sentinel = allocate_nonzero<ConstCastPtrSentinel>(1); | |
| 10558 | result.data.bad_ptr_sentinel->wanted_type = wanted_ptr_type; | |
| 10559 | result.data.bad_ptr_sentinel->actual_type = actual_ptr_type; | |
| 10560 | return result; | |
| 10561 | } | |
| 10536 | 10562 | if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) && |
| 10537 | 10563 | (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) && |
| 10538 | 10564 | actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host && |
| ... | ... | @@ -14441,6 +14467,32 @@ static bool optional_value_is_null(ZigValue *val) { |
| 14441 | 14467 | } |
| 14442 | 14468 | } |
| 14443 | 14469 | |
| 14470 | static void set_optional_value_to_null(ZigValue *val) { | |
| 14471 | assert(val->special == ConstValSpecialStatic); | |
| 14472 | if (val->type->id == ZigTypeIdNull) return; // nothing to do | |
| 14473 | assert(val->type->id == ZigTypeIdOptional); | |
| 14474 | if (get_codegen_ptr_type(val->type) != nullptr) { | |
| 14475 | val->data.x_ptr.special = ConstPtrSpecialNull; | |
| 14476 | } else if (is_opt_err_set(val->type)) { | |
| 14477 | val->data.x_err_set = nullptr; | |
| 14478 | } else { | |
| 14479 | val->data.x_optional = nullptr; | |
| 14480 | } | |
| 14481 | } | |
| 14482 | ||
| 14483 | static void set_optional_payload(ZigValue *opt_val, ZigValue *payload) { | |
| 14484 | assert(opt_val->special == ConstValSpecialStatic); | |
| 14485 | assert(opt_val->type->id == ZigTypeIdOptional); | |
| 14486 | if (payload == nullptr) { | |
| 14487 | set_optional_value_to_null(opt_val); | |
| 14488 | } else if (is_opt_err_set(opt_val->type)) { | |
| 14489 | assert(payload->type->id == ZigTypeIdErrorSet); | |
| 14490 | opt_val->data.x_err_set = payload->data.x_err_set; | |
| 14491 | } else { | |
| 14492 | opt_val->data.x_optional = payload; | |
| 14493 | } | |
| 14494 | } | |
| 14495 | ||
| 14444 | 14496 | static IrInstruction *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type, |
| 14445 | 14497 | ZigValue *op1_val, ZigValue *op2_val, IrInstructionBinOp *bin_op_instruction, IrBinOp op_id, |
| 14446 | 14498 | bool one_possible_value) { |
| ... | ... | @@ -19313,6 +19365,20 @@ static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_ali |
| 19313 | 19365 | ptr_type->data.pointer.sentinel); |
| 19314 | 19366 | } |
| 19315 | 19367 | |
| 19368 | static ZigType *adjust_ptr_sentinel(CodeGen *g, ZigType *ptr_type, ZigValue *new_sentinel) { | |
| 19369 | assert(ptr_type->id == ZigTypeIdPointer); | |
| 19370 | return get_pointer_to_type_extra2(g, | |
| 19371 | ptr_type->data.pointer.child_type, | |
| 19372 | ptr_type->data.pointer.is_const, ptr_type->data.pointer.is_volatile, | |
| 19373 | ptr_type->data.pointer.ptr_len, | |
| 19374 | ptr_type->data.pointer.explicit_alignment, | |
| 19375 | ptr_type->data.pointer.bit_offset_in_host, ptr_type->data.pointer.host_int_bytes, | |
| 19376 | ptr_type->data.pointer.allow_zero, | |
| 19377 | ptr_type->data.pointer.vector_index, | |
| 19378 | ptr_type->data.pointer.inferred_struct_field, | |
| 19379 | new_sentinel); | |
| 19380 | } | |
| 19381 | ||
| 19316 | 19382 | static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) { |
| 19317 | 19383 | assert(is_slice(slice_type)); |
| 19318 | 19384 | ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index]->type_entry, |
| ... | ... | @@ -22691,7 +22757,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent |
| 22691 | 22757 | fields[6]->special = ConstValSpecialStatic; |
| 22692 | 22758 | if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) { |
| 22693 | 22759 | fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type); |
| 22694 | fields[6]->data.x_optional = attrs_type->data.pointer.sentinel; | |
| 22760 | set_optional_payload(fields[6], attrs_type->data.pointer.sentinel); | |
| 22695 | 22761 | } else { |
| 22696 | 22762 | fields[6]->type = ira->codegen->builtin_types.entry_null; |
| 22697 | 22763 | } |
| ... | ... | @@ -25051,50 +25117,72 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction |
| 25051 | 25117 | end = nullptr; |
| 25052 | 25118 | } |
| 25053 | 25119 | |
| 25054 | ZigType *return_type; | |
| 25120 | ZigType *non_sentinel_slice_ptr_type; | |
| 25121 | ZigType *elem_type; | |
| 25055 | 25122 | |
| 25056 | 25123 | if (array_type->id == ZigTypeIdArray) { |
| 25124 | elem_type = array_type->data.array.child_type; | |
| 25057 | 25125 | bool is_comptime_const = ptr_ptr->value->special == ConstValSpecialStatic && |
| 25058 | 25126 | ptr_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst; |
| 25059 | ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, array_type->data.array.child_type, | |
| 25127 | non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, elem_type, | |
| 25060 | 25128 | ptr_ptr_type->data.pointer.is_const || is_comptime_const, |
| 25061 | 25129 | ptr_ptr_type->data.pointer.is_volatile, |
| 25062 | 25130 | PtrLenUnknown, |
| 25063 | 25131 | ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false); |
| 25064 | return_type = get_slice_type(ira->codegen, slice_ptr_type); | |
| 25065 | 25132 | } else if (array_type->id == ZigTypeIdPointer) { |
| 25066 | 25133 | if (array_type->data.pointer.ptr_len == PtrLenSingle) { |
| 25067 | 25134 | ZigType *main_type = array_type->data.pointer.child_type; |
| 25068 | 25135 | if (main_type->id == ZigTypeIdArray) { |
| 25069 | ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen, | |
| 25070 | main_type->data.pointer.child_type, | |
| 25136 | elem_type = main_type->data.pointer.child_type; | |
| 25137 | non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen, | |
| 25138 | elem_type, | |
| 25071 | 25139 | array_type->data.pointer.is_const, array_type->data.pointer.is_volatile, |
| 25072 | 25140 | PtrLenUnknown, |
| 25073 | 25141 | array_type->data.pointer.explicit_alignment, 0, 0, false); |
| 25074 | return_type = get_slice_type(ira->codegen, slice_ptr_type); | |
| 25075 | 25142 | } else { |
| 25076 | 25143 | ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer")); |
| 25077 | 25144 | return ira->codegen->invalid_instruction; |
| 25078 | 25145 | } |
| 25079 | 25146 | } else { |
| 25147 | elem_type = array_type->data.pointer.child_type; | |
| 25080 | 25148 | if (array_type->data.pointer.ptr_len == PtrLenC) { |
| 25081 | 25149 | array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown); |
| 25082 | 25150 | } |
| 25083 | return_type = get_slice_type(ira->codegen, array_type); | |
| 25151 | ZigType *maybe_sentineled_slice_ptr_type = array_type; | |
| 25152 | non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); | |
| 25084 | 25153 | if (!end) { |
| 25085 | 25154 | ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value")); |
| 25086 | 25155 | return ira->codegen->invalid_instruction; |
| 25087 | 25156 | } |
| 25088 | 25157 | } |
| 25089 | 25158 | } else if (is_slice(array_type)) { |
| 25090 | ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; | |
| 25091 | return_type = get_slice_type(ira->codegen, ptr_type); | |
| 25159 | ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry; | |
| 25160 | non_sentinel_slice_ptr_type = adjust_ptr_sentinel(ira->codegen, maybe_sentineled_slice_ptr_type, nullptr); | |
| 25161 | elem_type = non_sentinel_slice_ptr_type->data.pointer.child_type; | |
| 25092 | 25162 | } else { |
| 25093 | 25163 | ir_add_error(ira, &instruction->base, |
| 25094 | 25164 | buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name))); |
| 25095 | 25165 | return ira->codegen->invalid_instruction; |
| 25096 | 25166 | } |
| 25097 | 25167 | |
| 25168 | ZigType *return_type; | |
| 25169 | ZigValue *sentinel_val = nullptr; | |
| 25170 | if (instruction->sentinel) { | |
| 25171 | IrInstruction *uncasted_sentinel = instruction->sentinel->child; | |
| 25172 | if (type_is_invalid(uncasted_sentinel->value->type)) | |
| 25173 | return ira->codegen->invalid_instruction; | |
| 25174 | IrInstruction *sentinel = ir_implicit_cast(ira, uncasted_sentinel, elem_type); | |
| 25175 | if (type_is_invalid(sentinel->value->type)) | |
| 25176 | return ira->codegen->invalid_instruction; | |
| 25177 | sentinel_val = ir_resolve_const(ira, sentinel, UndefBad); | |
| 25178 | if (sentinel_val == nullptr) | |
| 25179 | return ira->codegen->invalid_instruction; | |
| 25180 | ZigType *slice_ptr_type = adjust_ptr_sentinel(ira->codegen, non_sentinel_slice_ptr_type, sentinel_val); | |
| 25181 | return_type = get_slice_type(ira->codegen, slice_ptr_type); | |
| 25182 | } else { | |
| 25183 | return_type = get_slice_type(ira->codegen, non_sentinel_slice_ptr_type); | |
| 25184 | } | |
| 25185 | ||
| 25098 | 25186 | if (instr_is_comptime(ptr_ptr) && |
| 25099 | 25187 | value_is_comptime(casted_start->value) && |
| 25100 | 25188 | (!end || value_is_comptime(end->value))) |
src/parser.cpp+7-1| ... | ... | @@ -2723,7 +2723,7 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) { |
| 2723 | 2723 | } |
| 2724 | 2724 | |
| 2725 | 2725 | // SuffixOp |
| 2726 | // <- LBRACKET Expr (DOT2 Expr?)? RBRACKET | |
| 2726 | // <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET | |
| 2727 | 2727 | // / DOT IDENTIFIER |
| 2728 | 2728 | // / DOTASTERISK |
| 2729 | 2729 | // / DOTQUESTIONMARK |
| ... | ... | @@ -2733,12 +2733,17 @@ static AstNode *ast_parse_suffix_op(ParseContext *pc) { |
| 2733 | 2733 | AstNode *start = ast_expect(pc, ast_parse_expr); |
| 2734 | 2734 | AstNode *end = nullptr; |
| 2735 | 2735 | if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) { |
| 2736 | AstNode *sentinel = nullptr; | |
| 2736 | 2737 | end = ast_parse_expr(pc); |
| 2738 | if (eat_token_if(pc, TokenIdColon) != nullptr) { | |
| 2739 | sentinel = ast_parse_expr(pc); | |
| 2740 | } | |
| 2737 | 2741 | expect_token(pc, TokenIdRBracket); |
| 2738 | 2742 | |
| 2739 | 2743 | AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket); |
| 2740 | 2744 | res->data.slice_expr.start = start; |
| 2741 | 2745 | res->data.slice_expr.end = end; |
| 2746 | res->data.slice_expr.sentinel = sentinel; | |
| 2742 | 2747 | return res; |
| 2743 | 2748 | } |
| 2744 | 2749 | |
| ... | ... | @@ -3041,6 +3046,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont |
| 3041 | 3046 | visit_field(&node->data.slice_expr.array_ref_expr, visit, context); |
| 3042 | 3047 | visit_field(&node->data.slice_expr.start, visit, context); |
| 3043 | 3048 | visit_field(&node->data.slice_expr.end, visit, context); |
| 3049 | visit_field(&node->data.slice_expr.sentinel, visit, context); | |
| 3044 | 3050 | break; |
| 3045 | 3051 | case NodeTypeFieldAccessExpr: |
| 3046 | 3052 | visit_field(&node->data.field_access_expr.struct_expr, visit, context); |
test/compile_errors.zig+11| ... | ... | @@ -2,6 +2,17 @@ const tests = @import("tests.zig"); |
| 2 | 2 | const builtin = @import("builtin"); |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(cases: *tests.CompileErrorContext) void { |
| 5 | cases.add("slice sentinel mismatch", | |
| 6 | \\fn foo() [:0]u8 { | |
| 7 | \\ var x: []u8 = undefined; | |
| 8 | \\ return x; | |
| 9 | \\} | |
| 10 | \\comptime { _ = foo; } | |
| 11 | , &[_][]const u8{ | |
| 12 | "tmp.zig:3:12: error: expected type '[:0]u8', found '[]u8'", | |
| 13 | "tmp.zig:3:12: note: destination pointer requires a terminating '0' sentinel", | |
| 14 | }); | |
| 15 | ||
| 5 | 16 | cases.add("intToPtr with misaligned address", |
| 6 | 17 | \\pub fn main() void { |
| 7 | 18 | \\ var y = @intToPtr([*]align(4) u8, 5); |
test/runtime_safety.zig+76-3| ... | ... | @@ -1,12 +1,85 @@ |
| 1 | 1 | const tests = @import("tests.zig"); |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(cases: *tests.CompareOutputContext) void { |
| 4 | cases.addRuntimeSafety("slice sentinel mismatch - optional pointers", | |
| 5 | \\const std = @import("std"); | |
| 6 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 7 | \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { | |
| 8 | \\ std.process.exit(126); // good | |
| 9 | \\ } | |
| 10 | \\ std.process.exit(0); // test failed | |
| 11 | \\} | |
| 12 | \\pub fn main() void { | |
| 13 | \\ var buf: [4]?*i32 = undefined; | |
| 14 | \\ const slice = buf[0..3 :null]; | |
| 15 | \\} | |
| 16 | ); | |
| 17 | ||
| 18 | cases.addRuntimeSafety("slice sentinel mismatch - floats", | |
| 19 | \\const std = @import("std"); | |
| 20 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 21 | \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { | |
| 22 | \\ std.process.exit(126); // good | |
| 23 | \\ } | |
| 24 | \\ std.process.exit(0); // test failed | |
| 25 | \\} | |
| 26 | \\pub fn main() void { | |
| 27 | \\ var buf: [4]f32 = undefined; | |
| 28 | \\ const slice = buf[0..3 :1.2]; | |
| 29 | \\} | |
| 30 | ); | |
| 31 | ||
| 32 | cases.addRuntimeSafety("pointer slice sentinel mismatch", | |
| 33 | \\const std = @import("std"); | |
| 34 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 35 | \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { | |
| 36 | \\ std.process.exit(126); // good | |
| 37 | \\ } | |
| 38 | \\ std.process.exit(0); // test failed | |
| 39 | \\} | |
| 40 | \\pub fn main() void { | |
| 41 | \\ var buf: [4]u8 = undefined; | |
| 42 | \\ const ptr = buf[0..].ptr; | |
| 43 | \\ const slice = ptr[0..3 :0]; | |
| 44 | \\} | |
| 45 | ); | |
| 46 | ||
| 47 | cases.addRuntimeSafety("slice slice sentinel mismatch", | |
| 48 | \\const std = @import("std"); | |
| 49 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 50 | \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { | |
| 51 | \\ std.process.exit(126); // good | |
| 52 | \\ } | |
| 53 | \\ std.process.exit(0); // test failed | |
| 54 | \\} | |
| 55 | \\pub fn main() void { | |
| 56 | \\ var buf: [4]u8 = undefined; | |
| 57 | \\ const slice = buf[0..]; | |
| 58 | \\ const slice2 = slice[0..3 :0]; | |
| 59 | \\} | |
| 60 | ); | |
| 61 | ||
| 62 | cases.addRuntimeSafety("array slice sentinel mismatch", | |
| 63 | \\const std = @import("std"); | |
| 64 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { | |
| 65 | \\ if (std.mem.eql(u8, message, "sentinel mismatch")) { | |
| 66 | \\ std.process.exit(126); // good | |
| 67 | \\ } | |
| 68 | \\ std.process.exit(0); // test failed | |
| 69 | \\} | |
| 70 | \\pub fn main() void { | |
| 71 | \\ var buf: [4]u8 = undefined; | |
| 72 | \\ const slice = buf[0..3 :0]; | |
| 73 | \\} | |
| 74 | ); | |
| 75 | ||
| 4 | 76 | cases.addRuntimeSafety("intToPtr with misaligned address", |
| 77 | \\const std = @import("std"); | |
| 5 | 78 | \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn { |
| 6 | \\ if (@import("std").mem.eql(u8, message, "incorrect alignment")) { | |
| 7 | \\ @import("std").os.exit(126); // good | |
| 79 | \\ if (std.mem.eql(u8, message, "incorrect alignment")) { | |
| 80 | \\ std.os.exit(126); // good | |
| 8 | 81 | \\ } |
| 9 | \\ @import("std").os.exit(0); // test failed | |
| 82 | \\ std.os.exit(0); // test failed | |
| 10 | 83 | \\} |
| 11 | 84 | \\pub fn main() void { |
| 12 | 85 | \\ var x: usize = 5; |
test/stage1/behavior/slice.zig+19| ... | ... | @@ -78,3 +78,22 @@ test "access len index of sentinel-terminated slice" { |
| 78 | 78 | S.doTheTest(); |
| 79 | 79 | comptime S.doTheTest(); |
| 80 | 80 | } |
| 81 | ||
| 82 | test "obtaining a null terminated slice" { | |
| 83 | // here we have a normal array | |
| 84 | var buf: [50]u8 = undefined; | |
| 85 | ||
| 86 | buf[0] = 'a'; | |
| 87 | buf[1] = 'b'; | |
| 88 | buf[2] = 'c'; | |
| 89 | buf[3] = 0; | |
| 90 | ||
| 91 | // now we obtain a null terminated slice: | |
| 92 | const ptr = buf[0..3 :0]; | |
| 93 | ||
| 94 | var runtime_len: usize = 3; | |
| 95 | const ptr2 = buf[0..runtime_len :0]; | |
| 96 | // ptr2 is a null-terminated slice | |
| 97 | comptime expect(@TypeOf(ptr2) == [:0]u8); | |
| 98 | comptime expect(@TypeOf(ptr2[0..2]) == []u8); | |
| 99 | } |