authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-12-21 14:11:16-05:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2019-12-21 14:11:16-05:00
logbc95c63cf227226861d7fbf63fa6c779fed8abf8
tree1e004f521047ead2fb52855da7bc4fa819020548
parent51cbd968203f348051b8c2bdc005ca5294a79ceb
parent290dc5d95b986464a5be91bb3fd0ada2dd0840ae
signature Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #3940 from ziglang/sentinel-slicing

fix std.mem.addNullByte and implement sentinel slicing

17 files changed, 334 insertions(+), 65 deletions(-)

lib/std/buffer.zig+2-2
...@@ -82,11 +82,11 @@ pub const Buffer = struct {...@@ -82,11 +82,11 @@ pub const Buffer = struct {
82 }82 }
8383
84 pub fn toSlice(self: Buffer) [:0]u8 {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 }
8787
88 pub fn toSliceConst(self: Buffer) [:0]const u8 {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 }
9191
92 pub fn shrink(self: *Buffer, new_len: usize) void {92 pub fn shrink(self: *Buffer, new_len: usize) void {
lib/std/cstr.zig+10-2
...@@ -31,13 +31,21 @@ fn testCStrFnsImpl() void {...@@ -31,13 +31,21 @@ fn testCStrFnsImpl() void {
31 testing.expect(mem.len(u8, "123456789") == 9);31 testing.expect(mem.len(u8, "123456789") == 9);
32}32}
3333
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/// Caller owns the returned memory.35/// Caller owns the returned memory.
36pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![:0]u8 {36pub fn addNullByte(allocator: *mem.Allocator, slice: []const u8) ![:0]u8 {
37 const result = try allocator.alloc(u8, slice.len + 1);37 const result = try allocator.alloc(u8, slice.len + 1);
38 mem.copy(u8, result, slice);38 mem.copy(u8, result, slice);
39 result[slice.len] = 0;39 result[slice.len] = 0;
40 return result;40 return result[0..slice.len :0];
41}
42
43test "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}
4250
43pub const NullTerminated2DArray = struct {51pub const NullTerminated2DArray = struct {
lib/std/debug.zig+19-15
...@@ -219,7 +219,7 @@ pub fn panic(comptime format: []const u8, args: var) noreturn {...@@ -219,7 +219,7 @@ pub fn panic(comptime format: []const u8, args: var) noreturn {
219}219}
220220
221/// TODO multithreaded awareness221/// TODO multithreaded awareness
222var panicking: u8 = 0; // TODO make this a bool222var panicking: u8 = 0;
223223
224pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn {224pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, comptime format: []const u8, args: var) noreturn {
225 @setCold(true);225 @setCold(true);
...@@ -230,21 +230,25 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c...@@ -230,21 +230,25 @@ pub fn panicExtra(trace: ?*const builtin.StackTrace, first_trace_addr: ?usize, c
230 resetSegfaultHandler();230 resetSegfaultHandler();
231 }231 }
232232
233 if (@atomicRmw(u8, &panicking, builtin.AtomicRmwOp.Xchg, 1, builtin.AtomicOrder.SeqCst) == 1) {233 switch (@atomicRmw(u8, &panicking, .Add, 1, .SeqCst)) {
234 // Panicked during a panic.234 0 => {
235235 const stderr = getStderrStream();
236 // TODO detect if a different thread caused the panic, because in that case236 stderr.print(format ++ "\n", args) catch os.abort();
237 // we would want to return here instead of calling abort, so that the thread237 if (trace) |t| {
238 // which first called panic can finish printing a stack trace.238 dumpStackTrace(t.*);
239 os.abort();239 }
240 }240 dumpCurrentStackTrace(first_trace_addr);
241 const stderr = getStderrStream();241 },
242 stderr.print(format ++ "\n", args) catch os.abort();242 1 => {
243 if (trace) |t| {243 // TODO detect if a different thread caused the panic, because in that case
244 dumpStackTrace(t.*);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 os.abort();252 os.abort();
249}253}
250254
lib/std/fs.zig+4-5
...@@ -221,16 +221,16 @@ pub const AtomicFile = struct {...@@ -221,16 +221,16 @@ pub const AtomicFile = struct {
221 }221 }
222222
223 tmp_path_buf[tmp_path_len] = 0;223 tmp_path_buf[tmp_path_len] = 0;
224 const tmp_path_slice = tmp_path_buf[0..tmp_path_len :0];
224225
225 const my_cwd = cwd();226 const my_cwd = cwd();
226227
227 while (true) {228 while (true) {
228 try crypto.randomBytes(rand_buf[0..]);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);
230231
231 // TODO https://github.com/ziglang/zig/issues/3770 to clean up this @ptrCast
232 const file = my_cwd.createFileC(232 const file = my_cwd.createFileC(
233 @ptrCast([*:0]u8, &tmp_path_buf),233 tmp_path_slice,
234 .{ .mode = mode, .exclusive = true },234 .{ .mode = mode, .exclusive = true },
235 ) catch |err| switch (err) {235 ) catch |err| switch (err) {
236 error.PathAlreadyExists => continue,236 error.PathAlreadyExists => continue,
...@@ -1488,8 +1488,7 @@ pub fn openSelfExe() OpenSelfExeError!File {...@@ -1488,8 +1488,7 @@ pub fn openSelfExe() OpenSelfExeError!File {
1488 var buf: [MAX_PATH_BYTES]u8 = undefined;1488 var buf: [MAX_PATH_BYTES]u8 = undefined;
1489 const self_exe_path = try selfExePath(&buf);1489 const self_exe_path = try selfExePath(&buf);
1490 buf[self_exe_path.len] = 0;1490 buf[self_exe_path.len] = 0;
1491 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/37701491 return openFileAbsoluteC(self_exe_path[0..self_exe_path.len :0].ptr, .{});
1492 return openFileAbsoluteC(@ptrCast([*:0]u8, self_exe_path.ptr), .{});
1493}1492}
14941493
1495test "openSelfExe" {1494test "openSelfExe" {
lib/std/mem.zig+5-4
...@@ -231,9 +231,10 @@ pub const Allocator = struct {...@@ -231,9 +231,10 @@ pub const Allocator = struct {
231 pub fn free(self: *Allocator, memory: var) void {231 pub fn free(self: *Allocator, memory: var) void {
232 const Slice = @typeInfo(@TypeOf(memory)).Pointer;232 const Slice = @typeInfo(@TypeOf(memory)).Pointer;
233 const bytes = @sliceToBytes(memory);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 const non_const_ptr = @intToPtr([*]u8, @ptrToInt(bytes.ptr));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 assert(shrink_result.len == 0);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,11 +364,11 @@ pub fn len(comptime T: type, ptr: [*:0]const T) usize {
363}364}
364365
365pub fn toSliceConst(comptime T: type, ptr: [*:0]const T) [:0]const T {366pub 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}
368369
369pub fn toSlice(comptime T: type, ptr: [*:0]T) [:0]T {370pub 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}
372373
373/// Returns true if all elements in a slice are equal to the scalar value provided374/// 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,9 +805,9 @@ pub fn execvpeC(file: [*:0]const u8, child_argv: [*:null]const ?[*:0]const u8, e
805 mem.copy(u8, &path_buf, search_path);805 mem.copy(u8, &path_buf, search_path);
806 path_buf[search_path.len] = '/';806 path_buf[search_path.len] = '/';
807 mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);807 mem.copy(u8, path_buf[search_path.len + 1 ..], file_slice);
808 path_buf[search_path.len + file_slice.len + 1] = 0;808 const path_len = search_path.len + file_slice.len + 1;
809 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770809 path_buf[path_len] = 0;
810 err = execveC(@ptrCast([*:0]u8, &path_buf), child_argv, envp);810 err = execveC(path_buf[0..path_len :0].ptr, child_argv, envp);
811 switch (err) {811 switch (err) {
812 error.AccessDenied => seen_eacces = true,812 error.AccessDenied => seen_eacces = true,
813 error.FileNotFound, error.NotDir => {},813 error.FileNotFound, error.NotDir => {},
...@@ -841,18 +841,14 @@ pub fn execvpe(...@@ -841,18 +841,14 @@ pub fn execvpe(
841 const arg_buf = try allocator.alloc(u8, arg.len + 1);841 const arg_buf = try allocator.alloc(u8, arg.len + 1);
842 @memcpy(arg_buf.ptr, arg.ptr, arg.len);842 @memcpy(arg_buf.ptr, arg.ptr, arg.len);
843 arg_buf[arg.len] = 0;843 arg_buf[arg.len] = 0;
844844 argv_buf[i] = arg_buf[0..arg.len :0].ptr;
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);
847 }845 }
848 argv_buf[argv_slice.len] = null;846 argv_buf[argv_slice.len] = null;
847 const argv_ptr = argv_buf[0..argv_slice.len :null].ptr;
849848
850 const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);849 const envp_buf = try createNullDelimitedEnvMap(allocator, env_map);
851 defer freeNullDelimitedEnvMap(allocator, envp_buf);850 defer freeNullDelimitedEnvMap(allocator, envp_buf);
852851
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 return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr);852 return execvpeC(argv_buf.ptr[0].?, argv_ptr, envp_buf.ptr);
857}853}
858854
...@@ -869,16 +865,13 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std....@@ -869,16 +865,13 @@ pub fn createNullDelimitedEnvMap(allocator: *mem.Allocator, env_map: *const std.
869 @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len);865 @memcpy(env_buf.ptr, pair.key.ptr, pair.key.len);
870 env_buf[pair.key.len] = '=';866 env_buf[pair.key.len] = '=';
871 @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);867 @memcpy(env_buf.ptr + pair.key.len + 1, pair.value.ptr, pair.value.len);
872 env_buf[env_buf.len - 1] = 0;868 const len = env_buf.len - 1;
873869 env_buf[len] = 0;
874 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770870 envp_buf[i] = env_buf[0..len :0].ptr;
875 envp_buf[i] = @ptrCast([*:0]u8, env_buf.ptr);
876 }871 }
877 assert(i == envp_count);872 assert(i == envp_count);
878 }873 }
879 // TODO avoid @ptrCast here using slice syntax with https://github.com/ziglang/zig/issues/3770874 return envp_buf[0..envp_count :null];
880 assert(envp_buf[envp_count] == null);
881 return @ptrCast([*:null]?[*:0]u8, envp_buf.ptr)[0..envp_count];
882}875}
883876
884pub fn freeNullDelimitedEnvMap(allocator: *mem.Allocator, envp_buf: []?[*:0]u8) void {877pub 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,6 +1701,7 @@ pub const Node = struct {
1701 pub const Slice = struct {1701 pub const Slice = struct {
1702 start: *Node,1702 start: *Node,
1703 end: ?*Node,1703 end: ?*Node,
1704 sentinel: ?*Node,
1704 };1705 };
1705 };1706 };
17061707
...@@ -1732,6 +1733,10 @@ pub const Node = struct {...@@ -1732,6 +1733,10 @@ pub const Node = struct {
1732 if (i < 1) return end;1733 if (i < 1) return end;
1733 i -= 1;1734 i -= 1;
1734 }1735 }
1736 if (range.sentinel) |sentinel| {
1737 if (i < 1) return sentinel;
1738 i -= 1;
1739 }
1735 },1740 },
1736 .ArrayInitializer => |*exprs| {1741 .ArrayInitializer => |*exprs| {
1737 if (i < exprs.len) return exprs.at(i).*;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,7 +2331,7 @@ fn parsePrefixTypeOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node
2331}2331}
23322332
2333/// SuffixOp2333/// SuffixOp
2334/// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET2334/// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
2335/// / DOT IDENTIFIER2335/// / DOT IDENTIFIER
2336/// / DOTASTERISK2336/// / DOTASTERISK
2337/// / DOTQUESTIONMARK2337/// / DOTQUESTIONMARK
...@@ -2349,11 +2349,16 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {...@@ -2349,11 +2349,16 @@ fn parseSuffixOp(arena: *Allocator, it: *TokenIterator, tree: *Tree) !?*Node {
23492349
2350 if (eatToken(it, .Ellipsis2) != null) {2350 if (eatToken(it, .Ellipsis2) != null) {
2351 const end_expr = try parseExpr(arena, it, tree);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 break :blk OpAndToken{2356 break :blk OpAndToken{
2353 .op = Op{2357 .op = Op{
2354 .Slice = Op.Slice{2358 .Slice = Op.Slice{
2355 .start = index_expr,2359 .start = index_expr,
2356 .end = end_expr,2360 .end = end_expr,
2361 .sentinel = sentinel,
2357 },2362 },
2358 },2363 },
2359 .token = try expectToken(it, tree, .RBracket),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,10 +419,13 @@ test "zig fmt: pointer of unknown length" {
419test "zig fmt: spaces around slice operator" {419test "zig fmt: spaces around slice operator" {
420 try testCanonical(420 try testCanonical(
421 \\var a = b[c..d];421 \\var a = b[c..d];
422 \\var a = b[c..d :0];
422 \\var a = b[c + 1 .. d];423 \\var a = b[c + 1 .. d];
423 \\var a = b[c + 1 ..];424 \\var a = b[c + 1 ..];
424 \\var a = b[c .. d + 1];425 \\var a = b[c .. d + 1];
426 \\var a = b[c .. d + 1 :0];
425 \\var a = b[c.a..d.e];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,7 +689,13 @@ fn renderExpression(
689 try renderExpression(allocator, stream, tree, indent, start_col, range.start, after_start_space);689 try renderExpression(allocator, stream, tree, indent, start_col, range.start, after_start_space);
690 try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // ..690 try renderToken(tree, stream, dotdot, indent, start_col, after_op_space); // ..
691 if (range.end) |end| {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 return renderToken(tree, stream, suffix_op.rtoken, indent, start_col, space); // ]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,6 +810,7 @@ struct AstNodeSliceExpr {
810 AstNode *array_ref_expr;810 AstNode *array_ref_expr;
811 AstNode *start;811 AstNode *start;
812 AstNode *end;812 AstNode *end;
813 AstNode *sentinel; // can be null
813};814};
814815
815struct AstNodeFieldAccessExpr {816struct AstNodeFieldAccessExpr {
...@@ -1778,6 +1779,7 @@ enum PanicMsgId {...@@ -1778,6 +1779,7 @@ enum PanicMsgId {
1778 PanicMsgIdResumedFnPendingAwait,1779 PanicMsgIdResumedFnPendingAwait,
1779 PanicMsgIdBadNoAsyncCall,1780 PanicMsgIdBadNoAsyncCall,
1780 PanicMsgIdResumeNotSuspendedFn,1781 PanicMsgIdResumeNotSuspendedFn,
1782 PanicMsgIdBadSentinel,
17811783
1782 PanicMsgIdCount,1784 PanicMsgIdCount,
1783};1785};
...@@ -3388,6 +3390,7 @@ struct IrInstructionSliceSrc {...@@ -3388,6 +3390,7 @@ struct IrInstructionSliceSrc {
3388 IrInstruction *ptr;3390 IrInstruction *ptr;
3389 IrInstruction *start;3391 IrInstruction *start;
3390 IrInstruction *end;3392 IrInstruction *end;
3393 IrInstruction *sentinel;
3391 ResultLoc *result_loc;3394 ResultLoc *result_loc;
3392};3395};
33933396
src/codegen.cpp+48-3
...@@ -941,6 +941,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {...@@ -941,6 +941,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
941 return buf_create_from_str("async function called with noasync suspended");941 return buf_create_from_str("async function called with noasync suspended");
942 case PanicMsgIdResumeNotSuspendedFn:942 case PanicMsgIdResumeNotSuspendedFn:
943 return buf_create_from_str("resumed a non-suspended function");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 zig_unreachable();947 zig_unreachable();
946}948}
...@@ -1419,6 +1421,27 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val,...@@ -1419,6 +1421,27 @@ static void add_bounds_check(CodeGen *g, LLVMValueRef target_val,
1419 LLVMPositionBuilderAtEnd(g->builder, ok_block);1421 LLVMPositionBuilderAtEnd(g->builder, ok_block);
1420}1422}
14211423
1424static 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
1422static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) {1445static LLVMValueRef gen_assert_zero(CodeGen *g, LLVMValueRef expr_val, ZigType *int_type) {
1423 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type));1446 LLVMValueRef zero = LLVMConstNull(get_llvm_type(g, int_type));
1424 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntEQ, expr_val, zero, "");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,6 +5267,9 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
52445267
5245 bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base);5268 bool want_runtime_safety = instruction->safety_check_on && ir_want_runtime_safety(g, &instruction->base);
52465269
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 if (array_type->id == ZigTypeIdArray ||5273 if (array_type->id == ZigTypeIdArray ||
5248 (array_type->id == ZigTypeIdPointer && array_type->data.pointer.ptr_len == PtrLenSingle))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,6 +5291,15 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
5265 LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type,5291 LLVMValueRef array_end = LLVMConstInt(g->builtin_types.entry_usize->llvm_type,
5266 array_type->data.array.len, false);5292 array_type->data.array.len, false);
5267 add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, array_end);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 if (!type_has_bits(array_type)) {5305 if (!type_has_bits(array_type)) {
...@@ -5297,6 +5332,10 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst...@@ -5297,6 +5332,10 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
52975332
5298 if (want_runtime_safety) {5333 if (want_runtime_safety) {
5299 add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);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 }
53015340
5302 if (type_has_bits(array_type)) {5341 if (type_has_bits(array_type)) {
...@@ -5337,18 +5376,24 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst...@@ -5337,18 +5376,24 @@ static LLVMValueRef ir_render_slice(CodeGen *g, IrExecutable *executable, IrInst
5337 end_val = prev_end;5376 end_val = prev_end;
5338 }5377 }
53395378
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 if (want_runtime_safety) {5382 if (want_runtime_safety) {
5341 assert(prev_end);5383 assert(prev_end);
5342 add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);5384 add_bounds_check(g, start_val, LLVMIntEQ, nullptr, LLVMIntULE, end_val);
5343 if (instruction->end) {5385 if (instruction->end) {
5344 add_bounds_check(g, end_val, LLVMIntEQ, nullptr, LLVMIntULE, prev_end);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 }
53475394
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 LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)ptr_index, "");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 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);5397 gen_store_untyped(g, slice_start_ptr, ptr_field_ptr, 0, false);
53535398
5354 LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, tmp_struct_ptr, (unsigned)len_index, "");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,18 +2967,21 @@ static IrInstruction *ir_build_memcpy(IrBuilder *irb, Scope *scope, AstNode *sou
2967}2967}
29682968
2969static IrInstruction *ir_build_slice_src(IrBuilder *irb, Scope *scope, AstNode *source_node,2969static 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 IrInstructionSliceSrc *instruction = ir_build_instruction<IrInstructionSliceSrc>(irb, scope, source_node);2973 IrInstructionSliceSrc *instruction = ir_build_instruction<IrInstructionSliceSrc>(irb, scope, source_node);
2973 instruction->ptr = ptr;2974 instruction->ptr = ptr;
2974 instruction->start = start;2975 instruction->start = start;
2975 instruction->end = end;2976 instruction->end = end;
2977 instruction->sentinel = sentinel;
2976 instruction->safety_check_on = safety_check_on;2978 instruction->safety_check_on = safety_check_on;
2977 instruction->result_loc = result_loc;2979 instruction->result_loc = result_loc;
29782980
2979 ir_ref_instruction(ptr, irb->current_basic_block);2981 ir_ref_instruction(ptr, irb->current_basic_block);
2980 ir_ref_instruction(start, irb->current_basic_block);2982 ir_ref_instruction(start, irb->current_basic_block);
2981 if (end) ir_ref_instruction(end, irb->current_basic_block);2983 if (end) ir_ref_instruction(end, irb->current_basic_block);
2984 if (sentinel) ir_ref_instruction(sentinel, irb->current_basic_block);
29822985
2983 return &instruction->base;2986 return &instruction->base;
2984}2987}
...@@ -8483,6 +8486,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -8483,6 +8486,7 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node,
8483 AstNode *array_node = slice_expr->array_ref_expr;8486 AstNode *array_node = slice_expr->array_ref_expr;
8484 AstNode *start_node = slice_expr->start;8487 AstNode *start_node = slice_expr->start;
8485 AstNode *end_node = slice_expr->end;8488 AstNode *end_node = slice_expr->end;
8489 AstNode *sentinel_node = slice_expr->sentinel;
84868490
8487 IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr);8491 IrInstruction *ptr_value = ir_gen_node_extra(irb, array_node, scope, LValPtr, nullptr);
8488 if (ptr_value == irb->codegen->invalid_instruction)8492 if (ptr_value == irb->codegen->invalid_instruction)
...@@ -8501,7 +8505,17 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node,...@@ -8501,7 +8505,17 @@ static IrInstruction *ir_gen_slice(IrBuilder *irb, Scope *scope, AstNode *node,
8501 end_value = nullptr;8505 end_value = nullptr;
8502 }8506 }
85038507
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 return ir_lval_wrap(irb, scope, slice, lval, result_loc);8519 return ir_lval_wrap(irb, scope, slice, lval, result_loc);
8506}8520}
85078521
...@@ -10533,6 +10547,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted...@@ -10533,6 +10547,18 @@ static ConstCastOnly types_match_const_cast_only(IrAnalyze *ira, ZigType *wanted
10533 result.id = ConstCastResultIdInvalid;10547 result.id = ConstCastResultIdInvalid;
10534 return result;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 if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&10562 if ((!actual_ptr_type->data.pointer.is_const || wanted_ptr_type->data.pointer.is_const) &&
10537 (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&10563 (!actual_ptr_type->data.pointer.is_volatile || wanted_ptr_type->data.pointer.is_volatile) &&
10538 actual_ptr_type->data.pointer.bit_offset_in_host == wanted_ptr_type->data.pointer.bit_offset_in_host &&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,6 +14467,32 @@ static bool optional_value_is_null(ZigValue *val) {
14441 }14467 }
14442}14468}
1444314469
14470static 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
14483static 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
14444static IrInstruction *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type,14496static IrInstruction *ir_evaluate_bin_op_cmp(IrAnalyze *ira, ZigType *resolved_type,
14445 ZigValue *op1_val, ZigValue *op2_val, IrInstructionBinOp *bin_op_instruction, IrBinOp op_id,14497 ZigValue *op1_val, ZigValue *op2_val, IrInstructionBinOp *bin_op_instruction, IrBinOp op_id,
14446 bool one_possible_value) {14498 bool one_possible_value) {
...@@ -19313,6 +19365,20 @@ static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_ali...@@ -19313,6 +19365,20 @@ static ZigType *adjust_ptr_align(CodeGen *g, ZigType *ptr_type, uint32_t new_ali
19313 ptr_type->data.pointer.sentinel);19365 ptr_type->data.pointer.sentinel);
19314}19366}
1931519367
19368static 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
19316static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) {19382static ZigType *adjust_slice_align(CodeGen *g, ZigType *slice_type, uint32_t new_align) {
19317 assert(is_slice(slice_type));19383 assert(is_slice(slice_type));
19318 ZigType *ptr_type = adjust_ptr_align(g, slice_type->data.structure.fields[slice_ptr_index]->type_entry,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,7 +22757,7 @@ static ZigValue *create_ptr_like_type_info(IrAnalyze *ira, ZigType *ptr_type_ent
22691 fields[6]->special = ConstValSpecialStatic;22757 fields[6]->special = ConstValSpecialStatic;
22692 if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) {22758 if (attrs_type->data.pointer.child_type->id != ZigTypeIdOpaque) {
22693 fields[6]->type = get_optional_type(ira->codegen, attrs_type->data.pointer.child_type);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 } else {22761 } else {
22696 fields[6]->type = ira->codegen->builtin_types.entry_null;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,50 +25117,72 @@ static IrInstruction *ir_analyze_instruction_slice(IrAnalyze *ira, IrInstruction
25051 end = nullptr;25117 end = nullptr;
25052 }25118 }
2505325119
25054 ZigType *return_type;25120 ZigType *non_sentinel_slice_ptr_type;
25121 ZigType *elem_type;
2505525122
25056 if (array_type->id == ZigTypeIdArray) {25123 if (array_type->id == ZigTypeIdArray) {
25124 elem_type = array_type->data.array.child_type;
25057 bool is_comptime_const = ptr_ptr->value->special == ConstValSpecialStatic &&25125 bool is_comptime_const = ptr_ptr->value->special == ConstValSpecialStatic &&
25058 ptr_ptr->value->data.x_ptr.mut == ConstPtrMutComptimeConst;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 ptr_ptr_type->data.pointer.is_const || is_comptime_const,25128 ptr_ptr_type->data.pointer.is_const || is_comptime_const,
25061 ptr_ptr_type->data.pointer.is_volatile,25129 ptr_ptr_type->data.pointer.is_volatile,
25062 PtrLenUnknown,25130 PtrLenUnknown,
25063 ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false);25131 ptr_ptr_type->data.pointer.explicit_alignment, 0, 0, false);
25064 return_type = get_slice_type(ira->codegen, slice_ptr_type);
25065 } else if (array_type->id == ZigTypeIdPointer) {25132 } else if (array_type->id == ZigTypeIdPointer) {
25066 if (array_type->data.pointer.ptr_len == PtrLenSingle) {25133 if (array_type->data.pointer.ptr_len == PtrLenSingle) {
25067 ZigType *main_type = array_type->data.pointer.child_type;25134 ZigType *main_type = array_type->data.pointer.child_type;
25068 if (main_type->id == ZigTypeIdArray) {25135 if (main_type->id == ZigTypeIdArray) {
25069 ZigType *slice_ptr_type = get_pointer_to_type_extra(ira->codegen,25136 elem_type = main_type->data.pointer.child_type;
25070 main_type->data.pointer.child_type,25137 non_sentinel_slice_ptr_type = get_pointer_to_type_extra(ira->codegen,
25138 elem_type,
25071 array_type->data.pointer.is_const, array_type->data.pointer.is_volatile,25139 array_type->data.pointer.is_const, array_type->data.pointer.is_volatile,
25072 PtrLenUnknown,25140 PtrLenUnknown,
25073 array_type->data.pointer.explicit_alignment, 0, 0, false);25141 array_type->data.pointer.explicit_alignment, 0, 0, false);
25074 return_type = get_slice_type(ira->codegen, slice_ptr_type);
25075 } else {25142 } else {
25076 ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer"));25143 ir_add_error(ira, &instruction->base, buf_sprintf("slice of single-item pointer"));
25077 return ira->codegen->invalid_instruction;25144 return ira->codegen->invalid_instruction;
25078 }25145 }
25079 } else {25146 } else {
25147 elem_type = array_type->data.pointer.child_type;
25080 if (array_type->data.pointer.ptr_len == PtrLenC) {25148 if (array_type->data.pointer.ptr_len == PtrLenC) {
25081 array_type = adjust_ptr_len(ira->codegen, array_type, PtrLenUnknown);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 if (!end) {25153 if (!end) {
25085 ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value"));25154 ir_add_error(ira, &instruction->base, buf_sprintf("slice of pointer must include end value"));
25086 return ira->codegen->invalid_instruction;25155 return ira->codegen->invalid_instruction;
25087 }25156 }
25088 }25157 }
25089 } else if (is_slice(array_type)) {25158 } else if (is_slice(array_type)) {
25090 ZigType *ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry;25159 ZigType *maybe_sentineled_slice_ptr_type = array_type->data.structure.fields[slice_ptr_index]->type_entry;
25091 return_type = get_slice_type(ira->codegen, ptr_type);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 } else {25162 } else {
25093 ir_add_error(ira, &instruction->base,25163 ir_add_error(ira, &instruction->base,
25094 buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));25164 buf_sprintf("slice of non-array type '%s'", buf_ptr(&array_type->name)));
25095 return ira->codegen->invalid_instruction;25165 return ira->codegen->invalid_instruction;
25096 }25166 }
2509725167
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 if (instr_is_comptime(ptr_ptr) &&25186 if (instr_is_comptime(ptr_ptr) &&
25099 value_is_comptime(casted_start->value) &&25187 value_is_comptime(casted_start->value) &&
25100 (!end || value_is_comptime(end->value)))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,7 +2723,7 @@ static AstNode *ast_parse_prefix_type_op(ParseContext *pc) {
2723}2723}
27242724
2725// SuffixOp2725// SuffixOp
2726// <- LBRACKET Expr (DOT2 Expr?)? RBRACKET2726// <- LBRACKET Expr (DOT2 (Expr (COLON Expr)?)?)? RBRACKET
2727// / DOT IDENTIFIER2727// / DOT IDENTIFIER
2728// / DOTASTERISK2728// / DOTASTERISK
2729// / DOTQUESTIONMARK2729// / DOTQUESTIONMARK
...@@ -2733,12 +2733,17 @@ static AstNode *ast_parse_suffix_op(ParseContext *pc) {...@@ -2733,12 +2733,17 @@ static AstNode *ast_parse_suffix_op(ParseContext *pc) {
2733 AstNode *start = ast_expect(pc, ast_parse_expr);2733 AstNode *start = ast_expect(pc, ast_parse_expr);
2734 AstNode *end = nullptr;2734 AstNode *end = nullptr;
2735 if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) {2735 if (eat_token_if(pc, TokenIdEllipsis2) != nullptr) {
2736 AstNode *sentinel = nullptr;
2736 end = ast_parse_expr(pc);2737 end = ast_parse_expr(pc);
2738 if (eat_token_if(pc, TokenIdColon) != nullptr) {
2739 sentinel = ast_parse_expr(pc);
2740 }
2737 expect_token(pc, TokenIdRBracket);2741 expect_token(pc, TokenIdRBracket);
27382742
2739 AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket);2743 AstNode *res = ast_create_node(pc, NodeTypeSliceExpr, lbracket);
2740 res->data.slice_expr.start = start;2744 res->data.slice_expr.start = start;
2741 res->data.slice_expr.end = end;2745 res->data.slice_expr.end = end;
2746 res->data.slice_expr.sentinel = sentinel;
2742 return res;2747 return res;
2743 }2748 }
27442749
...@@ -3041,6 +3046,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont...@@ -3041,6 +3046,7 @@ void ast_visit_node_children(AstNode *node, void (*visit)(AstNode **, void *cont
3041 visit_field(&node->data.slice_expr.array_ref_expr, visit, context);3046 visit_field(&node->data.slice_expr.array_ref_expr, visit, context);
3042 visit_field(&node->data.slice_expr.start, visit, context);3047 visit_field(&node->data.slice_expr.start, visit, context);
3043 visit_field(&node->data.slice_expr.end, visit, context);3048 visit_field(&node->data.slice_expr.end, visit, context);
3049 visit_field(&node->data.slice_expr.sentinel, visit, context);
3044 break;3050 break;
3045 case NodeTypeFieldAccessExpr:3051 case NodeTypeFieldAccessExpr:
3046 visit_field(&node->data.field_access_expr.struct_expr, visit, context);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,6 +2,17 @@ const tests = @import("tests.zig");
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4pub fn addCases(cases: *tests.CompileErrorContext) void {4pub 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 cases.add("intToPtr with misaligned address",16 cases.add("intToPtr with misaligned address",
6 \\pub fn main() void {17 \\pub fn main() void {
7 \\ var y = @intToPtr([*]align(4) u8, 5);18 \\ var y = @intToPtr([*]align(4) u8, 5);
test/runtime_safety.zig+76-3
...@@ -1,12 +1,85 @@...@@ -1,12 +1,85 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompareOutputContext) void {3pub 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 cases.addRuntimeSafety("intToPtr with misaligned address",76 cases.addRuntimeSafety("intToPtr with misaligned address",
77 \\const std = @import("std");
5 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {78 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
6 \\ if (@import("std").mem.eql(u8, message, "incorrect alignment")) {79 \\ if (std.mem.eql(u8, message, "incorrect alignment")) {
7 \\ @import("std").os.exit(126); // good80 \\ std.os.exit(126); // good
8 \\ }81 \\ }
9 \\ @import("std").os.exit(0); // test failed82 \\ std.os.exit(0); // test failed
10 \\}83 \\}
11 \\pub fn main() void {84 \\pub fn main() void {
12 \\ var x: usize = 5;85 \\ var x: usize = 5;
test/stage1/behavior/slice.zig+19
...@@ -78,3 +78,22 @@ test "access len index of sentinel-terminated slice" {...@@ -78,3 +78,22 @@ test "access len index of sentinel-terminated slice" {
78 S.doTheTest();78 S.doTheTest();
79 comptime S.doTheTest();79 comptime S.doTheTest();
80}80}
81
82test "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}