authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2023-05-11 08:59:44-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-05-11 08:59:44-07:00
log7f7bd206dc8309ca767b47fb97bb9e7c2dc882c3
tree2d24acb80954ec2d33863befa9ce48ecd3075e72
parent5512455974a9dda5d2a86a81e4a5cc520cb7afa8
parent4d296debefccbd80f2007685a27386b7434464dd
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15519 from dweiller/issue-15482

Optimize lowering of `s[start..][0..len]`

22 files changed, 325 insertions(+), 31 deletions(-)

doc/langref.html.in+8
...@@ -2953,6 +2953,14 @@ test "basic slices" {...@@ -2953,6 +2953,14 @@ test "basic slices" {
2953 const array_ptr = array[0..array.len];2953 const array_ptr = array[0..array.len];
2954 try expect(@TypeOf(array_ptr) == *[array.len]i32);2954 try expect(@TypeOf(array_ptr) == *[array.len]i32);
29552955
2956 // You can perform a slice-by-length by slicing twice. This allows the compiler
2957 // to perform some optimisations like recognising a comptime-known length when
2958 // the start position is only known at runtime.
2959 var runtime_start: usize = 1;
2960 const length = 2;
2961 const array_ptr_len = array[runtime_start..][0..length];
2962 try expect(@TypeOf(array_ptr_len) == *[length]i32);
2963
2956 // Using the address-of operator on a slice gives a single-item pointer,2964 // Using the address-of operator on a slice gives a single-item pointer,
2957 // while using the `ptr` field gives a many-item pointer.2965 // while using the `ptr` field gives a many-item pointer.
2958 try expect(@TypeOf(slice.ptr) == [*]i32);2966 try expect(@TypeOf(slice.ptr) == [*]i32);
lib/docs/main.js+17
...@@ -1097,6 +1097,23 @@ const NAV_MODES = {...@@ -1097,6 +1097,23 @@ const NAV_MODES = {
1097 payloadHtml += decl + "[" + start + ".." + end + sentinel + "]";1097 payloadHtml += decl + "[" + start + ".." + end + sentinel + "]";
1098 return payloadHtml;1098 return payloadHtml;
1099 }1099 }
1100 case "sliceLength": {
1101 let payloadHtml = "";
1102 const lhsExpr = zigAnalysis.exprs[expr.sliceLength.lhs];
1103 const startExpr = zigAnalysis.exprs[expr.sliceLength.start];
1104 const lenExpr = zigAnalysis.exprs[expr.sliceLength.len];
1105 let decl = exprName(lhsExpr, opts);
1106 let start = exprName(startExpr, opts);
1107 let len = exprName(lenExpr, opts);
1108 let sentinel = "";
1109 if (expr.sliceLength["sentinel"]) {
1110 const sentinelExpr = zigAnalysis.exprs[expr.sliceLength.sentinel];
1111 let sentinel_ = exprName(sentinelExpr, options);
1112 sentinel += " :" + sentinel_;
1113 }
1114 payloadHtml += decl + "[" + start + "..][0.." + len + sentinel + "]";
1115 return payloadHtml;
1116 }
1100 case "sliceIndex": {1117 case "sliceIndex": {
1101 const sliceIndex = zigAnalysis.exprs[expr.sliceIndex];1118 const sliceIndex = zigAnalysis.exprs[expr.sliceIndex];
1102 return exprName(sliceIndex, opts, opts);1119 return exprName(sliceIndex, opts, opts);
lib/std/Build/Cache/DepTokenizer.zig+1-1
...@@ -974,7 +974,7 @@ fn hexDump(out: anytype, bytes: []const u8) !void {...@@ -974,7 +974,7 @@ fn hexDump(out: anytype, bytes: []const u8) !void {
974 var line: usize = 0;974 var line: usize = 0;
975 var offset: usize = 0;975 var offset: usize = 0;
976 while (line < n16) : (line += 1) {976 while (line < n16) : (line += 1) {
977 try hexDump16(out, offset, bytes[offset .. offset + 16]);977 try hexDump16(out, offset, bytes[offset..][0..16]);
978 offset += 16;978 offset += 16;
979 }979 }
980980
lib/std/array_list.zig+2-2
...@@ -173,7 +173,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {...@@ -173,7 +173,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type {
173 @memcpy(self.items[i..][0..items.len], items);173 @memcpy(self.items[i..][0..items.len], items);
174 }174 }
175175
176 /// Replace range of elements `list[start..start+len]` with `new_items`.176 /// Replace range of elements `list[start..][0..len]` with `new_items`.
177 /// Grows list if `len < new_items.len`.177 /// Grows list if `len < new_items.len`.
178 /// Shrinks list if `len > new_items.len`.178 /// Shrinks list if `len > new_items.len`.
179 /// Invalidates pointers if this ArrayList is resized.179 /// Invalidates pointers if this ArrayList is resized.
...@@ -654,7 +654,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ...@@ -654,7 +654,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ
654 @memcpy(self.items[i..][0..items.len], items);654 @memcpy(self.items[i..][0..items.len], items);
655 }655 }
656656
657 /// Replace range of elements `list[start..start+len]` with `new_items`657 /// Replace range of elements `list[start..][0..len]` with `new_items`
658 /// Grows list if `len < new_items.len`.658 /// Grows list if `len < new_items.len`.
659 /// Shrinks list if `len > new_items.len`659 /// Shrinks list if `len > new_items.len`
660 /// Invalidates pointers if this ArrayList is resized.660 /// Invalidates pointers if this ArrayList is resized.
lib/std/bounded_array.zig+1-1
...@@ -168,7 +168,7 @@ pub fn BoundedArrayAligned(...@@ -168,7 +168,7 @@ pub fn BoundedArrayAligned(
168 @memcpy(self.slice()[i..][0..items.len], items);168 @memcpy(self.slice()[i..][0..items.len], items);
169 }169 }
170170
171 /// Replace range of elements `slice[start..start+len]` with `new_items`.171 /// Replace range of elements `slice[start..][0..len]` with `new_items`.
172 /// Grows slice if `len < new_items.len`.172 /// Grows slice if `len < new_items.len`.
173 /// Shrinks slice if `len > new_items.len`.173 /// Shrinks slice if `len > new_items.len`.
174 pub fn replaceRange(174 pub fn replaceRange(
lib/std/compress/deflate/decompressor.zig+1-1
...@@ -591,7 +591,7 @@ pub fn Decompressor(comptime ReaderType: type) type {...@@ -591,7 +591,7 @@ pub fn Decompressor(comptime ReaderType: type) type {
591 }591 }
592592
593 if (!try self.hd1.init(self.allocator, self.bits[0..nlit]) or593 if (!try self.hd1.init(self.allocator, self.bits[0..nlit]) or
594 !try self.hd2.init(self.allocator, self.bits[nlit .. nlit + ndist]))594 !try self.hd2.init(self.allocator, self.bits[nlit..][0..ndist]))
595 {595 {
596 corrupt_input_error_offset = self.roffset;596 corrupt_input_error_offset = self.roffset;
597 self.err = InflateError.CorruptInput;597 self.err = InflateError.CorruptInput;
lib/std/compress/deflate/huffman_bit_writer.zig+3-3
...@@ -139,7 +139,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {...@@ -139,7 +139,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {
139 self.bits >>= 48;139 self.bits >>= 48;
140 self.nbits -= 48;140 self.nbits -= 48;
141 var n = self.nbytes;141 var n = self.nbytes;
142 var bytes = self.bytes[n .. n + 6];142 var bytes = self.bytes[n..][0..6];
143 bytes[0] = @truncate(u8, bits);143 bytes[0] = @truncate(u8, bits);
144 bytes[1] = @truncate(u8, bits >> 8);144 bytes[1] = @truncate(u8, bits >> 8);
145 bytes[2] = @truncate(u8, bits >> 16);145 bytes[2] = @truncate(u8, bits >> 16);
...@@ -344,7 +344,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {...@@ -344,7 +344,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {
344 self.bits >>= 48;344 self.bits >>= 48;
345 self.nbits -= 48;345 self.nbits -= 48;
346 var n = self.nbytes;346 var n = self.nbytes;
347 var bytes = self.bytes[n .. n + 6];347 var bytes = self.bytes[n..][0..6];
348 bytes[0] = @truncate(u8, bits);348 bytes[0] = @truncate(u8, bits);
349 bytes[1] = @truncate(u8, bits >> 8);349 bytes[1] = @truncate(u8, bits >> 8);
350 bytes[2] = @truncate(u8, bits >> 16);350 bytes[2] = @truncate(u8, bits >> 16);
...@@ -751,7 +751,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {...@@ -751,7 +751,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type {
751 var bits = self.bits;751 var bits = self.bits;
752 self.bits >>= 48;752 self.bits >>= 48;
753 self.nbits -= 48;753 self.nbits -= 48;
754 var bytes = self.bytes[n .. n + 6];754 var bytes = self.bytes[n..][0..6];
755 bytes[0] = @truncate(u8, bits);755 bytes[0] = @truncate(u8, bits);
756 bytes[1] = @truncate(u8, bits >> 8);756 bytes[1] = @truncate(u8, bits >> 8);
757 bytes[2] = @truncate(u8, bits >> 16);757 bytes[2] = @truncate(u8, bits >> 16);
lib/std/hash/crc.zig+1-1
...@@ -160,7 +160,7 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type {...@@ -160,7 +160,7 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type {
160 pub fn update(self: *Self, input: []const u8) void {160 pub fn update(self: *Self, input: []const u8) void {
161 var i: usize = 0;161 var i: usize = 0;
162 while (i + 8 <= input.len) : (i += 8) {162 while (i + 8 <= input.len) : (i += 8) {
163 const p = input[i .. i + 8];163 const p = input[i..][0..8];
164164
165 // Unrolling this way gives ~50Mb/s increase165 // Unrolling this way gives ~50Mb/s increase
166 self.crc ^= std.mem.readIntLittle(u32, p[0..4]);166 self.crc ^= std.mem.readIntLittle(u32, p[0..4]);
lib/std/hash/wyhash.zig+1-1
...@@ -65,7 +65,7 @@ const WyhashStateless = struct {...@@ -65,7 +65,7 @@ const WyhashStateless = struct {
6565
66 var off: usize = 0;66 var off: usize = 0;
67 while (off < b.len) : (off += 32) {67 while (off < b.len) : (off += 32) {
68 @call(.always_inline, self.round, .{b[off .. off + 32]});68 @call(.always_inline, self.round, .{b[off..][0..32]});
69 }69 }
7070
71 self.msg_len += b.len;71 self.msg_len += b.len;
lib/std/json.zig+3-3
...@@ -63,7 +63,7 @@ fn encodesTo(decoded: []const u8, encoded: []const u8) bool {...@@ -63,7 +63,7 @@ fn encodesTo(decoded: []const u8, encoded: []const u8) bool {
63 var buf: [4]u8 = undefined;63 var buf: [4]u8 = undefined;
64 const len = std.unicode.utf8Encode(codepoint, &buf) catch unreachable;64 const len = std.unicode.utf8Encode(codepoint, &buf) catch unreachable;
65 if (i + len > decoded.len) return false;65 if (i + len > decoded.len) return false;
66 if (!mem.eql(u8, decoded[i .. i + len], buf[0..len])) return false;66 if (!mem.eql(u8, decoded[i..][0..len], buf[0..len])) return false;
67 i += len;67 i += len;
68 }68 }
69 }69 }
...@@ -2285,10 +2285,10 @@ pub fn encodeJsonStringChars(chars: []const u8, options: StringifyOptions, write...@@ -2285,10 +2285,10 @@ pub fn encodeJsonStringChars(chars: []const u8, options: StringifyOptions, write
2285 const ulen = std.unicode.utf8ByteSequenceLength(chars[i]) catch unreachable;2285 const ulen = std.unicode.utf8ByteSequenceLength(chars[i]) catch unreachable;
2286 // control characters (only things left with 1 byte length) should always be printed as unicode escapes2286 // control characters (only things left with 1 byte length) should always be printed as unicode escapes
2287 if (ulen == 1 or options.string.String.escape_unicode) {2287 if (ulen == 1 or options.string.String.escape_unicode) {
2288 const codepoint = std.unicode.utf8Decode(chars[i .. i + ulen]) catch unreachable;2288 const codepoint = std.unicode.utf8Decode(chars[i..][0..ulen]) catch unreachable;
2289 try outputUnicodeEscape(codepoint, writer);2289 try outputUnicodeEscape(codepoint, writer);
2290 } else {2290 } else {
2291 try writer.writeAll(chars[i .. i + ulen]);2291 try writer.writeAll(chars[i..][0..ulen]);
2292 }2292 }
2293 i += ulen - 1;2293 i += ulen - 1;
2294 },2294 },
lib/std/math/big/int.zig+1-1
...@@ -4035,7 +4035,7 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void {...@@ -4035,7 +4035,7 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void {
40354035
4036 for (x_norm, 0..) |v, i| {4036 for (x_norm, 0..) |v, i| {
4037 // Compute and add the squares4037 // Compute and add the squares
4038 const overflow = llmulLimb(.add, r[2 * i ..], x[i .. i + 1], v);4038 const overflow = llmulLimb(.add, r[2 * i ..], x[i..][0..1], v);
4039 assert(!overflow);4039 assert(!overflow);
4040 }4040 }
4041}4041}
lib/std/net.zig+1-1
...@@ -1701,7 +1701,7 @@ fn dnsParse(...@@ -1701,7 +1701,7 @@ fn dnsParse(
1701 p += @as(usize, 1) + @boolToInt(p[0] != 0);1701 p += @as(usize, 1) + @boolToInt(p[0] != 0);
1702 const len = p[8] * @as(usize, 256) + p[9];1702 const len = p[8] * @as(usize, 256) + p[9];
1703 if (@ptrToInt(p) + len > @ptrToInt(r.ptr) + r.len) return error.InvalidDnsPacket;1703 if (@ptrToInt(p) + len > @ptrToInt(r.ptr) + r.len) return error.InvalidDnsPacket;
1704 try callback(ctx, p[1], p[10 .. 10 + len], r);1704 try callback(ctx, p[1], p[10..][0..len], r);
1705 p += 10 + len;1705 p += 10 + len;
1706 }1706 }
1707}1707}
lib/std/os/windows.zig+2-2
...@@ -835,14 +835,14 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin...@@ -835,14 +835,14 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin
835 const len = buf.SubstituteNameLength >> 1;835 const len = buf.SubstituteNameLength >> 1;
836 const path_buf = @as([*]const u16, &buf.PathBuffer);836 const path_buf = @as([*]const u16, &buf.PathBuffer);
837 const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;837 const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0;
838 return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer);838 return parseReadlinkPath(path_buf[offset..][0..len], is_relative, out_buffer);
839 },839 },
840 IO_REPARSE_TAG_MOUNT_POINT => {840 IO_REPARSE_TAG_MOUNT_POINT => {
841 const buf = @ptrCast(*const MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));841 const buf = @ptrCast(*const MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0]));
842 const offset = buf.SubstituteNameOffset >> 1;842 const offset = buf.SubstituteNameOffset >> 1;
843 const len = buf.SubstituteNameLength >> 1;843 const len = buf.SubstituteNameLength >> 1;
844 const path_buf = @as([*]const u16, &buf.PathBuffer);844 const path_buf = @as([*]const u16, &buf.PathBuffer);
845 return parseReadlinkPath(path_buf[offset .. offset + len], false, out_buffer);845 return parseReadlinkPath(path_buf[offset..][0..len], false, out_buffer);
846 },846 },
847 else => |value| {847 else => |value| {
848 std.debug.print("unsupported symlink type: {}", .{value});848 std.debug.print("unsupported symlink type: {}", .{value});
lib/std/testing.zig+1-1
...@@ -941,7 +941,7 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void {...@@ -941,7 +941,7 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void {
941fn printWithVisibleNewlines(source: []const u8) void {941fn printWithVisibleNewlines(source: []const u8) void {
942 var i: usize = 0;942 var i: usize = 0;
943 while (std.mem.indexOfScalar(u8, source[i..], '\n')) |nl| : (i += nl + 1) {943 while (std.mem.indexOfScalar(u8, source[i..], '\n')) |nl| : (i += nl + 1) {
944 printLine(source[i .. i + nl]);944 printLine(source[i..][0..nl]);
945 }945 }
946 print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX)946 print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX)
947}947}
lib/std/unicode.zig+1-1
...@@ -185,7 +185,7 @@ pub fn utf8CountCodepoints(s: []const u8) !usize {...@@ -185,7 +185,7 @@ pub fn utf8CountCodepoints(s: []const u8) !usize {
185185
186 switch (n) {186 switch (n) {
187 1 => {}, // ASCII, no validation needed187 1 => {}, // ASCII, no validation needed
188 else => _ = try utf8Decode(s[i .. i + n]),188 else => _ = try utf8Decode(s[i..][0..n]),
189 }189 }
190190
191 i += n;191 i += n;
src/AstGen.zig+54-2
...@@ -849,10 +849,35 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -849,10 +849,35 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
849 return rvalue(gz, ri, result, node);849 return rvalue(gz, ri, result, node);
850 },850 },
851 .slice => {851 .slice => {
852 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
853 const lhs_node = node_datas[node].lhs;
854 const lhs_tag = node_tags[lhs_node];
855 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
856 const lhs_is_open_slice = lhs_tag == .slice_open or
857 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
858 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
859 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
860
861 const start = if (lhs_is_slice_sentinel) start: {
862 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
863 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
864 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
865
866 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
867 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
868 try emitDbgStmt(gz, cursor);
869 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
870 .lhs = lhs,
871 .start = start,
872 .len = len,
873 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
874 .sentinel = .none,
875 });
876 return rvalue(gz, ri, result, node);
877 }
852 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);878 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
853879
854 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);880 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
855 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice);
856 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);881 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
857 const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);882 const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end);
858 try emitDbgStmt(gz, cursor);883 try emitDbgStmt(gz, cursor);
...@@ -864,10 +889,36 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE...@@ -864,10 +889,36 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
864 return rvalue(gz, ri, result, node);889 return rvalue(gz, ri, result, node);
865 },890 },
866 .slice_sentinel => {891 .slice_sentinel => {
892 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
893 const lhs_node = node_datas[node].lhs;
894 const lhs_tag = node_tags[lhs_node];
895 const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel;
896 const lhs_is_open_slice = lhs_tag == .slice_open or
897 (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0);
898 if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) {
899 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs);
900
901 const start = if (lhs_is_slice_sentinel) start: {
902 const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel);
903 break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start);
904 } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs);
905
906 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
907 const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
908 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
909 try emitDbgStmt(gz, cursor);
910 const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{
911 .lhs = lhs,
912 .start = start,
913 .len = len,
914 .start_src_node_offset = gz.nodeIndexToRelative(lhs_node),
915 .sentinel = sentinel,
916 });
917 return rvalue(gz, ri, result, node);
918 }
867 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);919 const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs);
868920
869 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);921 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
870 const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel);
871 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);922 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start);
872 const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;923 const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none;
873 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);924 const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel);
...@@ -2557,6 +2608,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As...@@ -2557,6 +2608,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2557 .slice_start,2608 .slice_start,
2558 .slice_end,2609 .slice_end,
2559 .slice_sentinel,2610 .slice_sentinel,
2611 .slice_length,
2560 .import,2612 .import,
2561 .switch_block,2613 .switch_block,
2562 .switch_cond,2614 .switch_cond,
src/Autodoc.zig+69
...@@ -758,6 +758,7 @@ const DocData = struct {...@@ -758,6 +758,7 @@ const DocData = struct {
758 string: []const u8, // direct value758 string: []const u8, // direct value
759 sliceIndex: usize,759 sliceIndex: usize,
760 slice: Slice,760 slice: Slice,
761 sliceLength: SliceLength,
761 cmpxchgIndex: usize,762 cmpxchgIndex: usize,
762 cmpxchg: Cmpxchg,763 cmpxchg: Cmpxchg,
763 builtin: Builtin,764 builtin: Builtin,
...@@ -794,6 +795,12 @@ const DocData = struct {...@@ -794,6 +795,12 @@ const DocData = struct {
794 end: ?usize = null,795 end: ?usize = null,
795 sentinel: ?usize = null, // index in `exprs`796 sentinel: ?usize = null, // index in `exprs`
796 };797 };
798 const SliceLength = struct {
799 lhs: usize,
800 start: usize,
801 len: usize,
802 sentinel: ?usize = null,
803 };
797 const Cmpxchg = struct {804 const Cmpxchg = struct {
798 name: []const u8,805 name: []const u8,
799 type: usize,806 type: usize,
...@@ -1296,6 +1303,68 @@ fn walkInstruction(...@@ -1296,6 +1303,68 @@ fn walkInstruction(
1296 .expr = .{ .sliceIndex = slice_index },1303 .expr = .{ .sliceIndex = slice_index },
1297 };1304 };
1298 },1305 },
1306 .slice_length => {
1307 const pl_node = data[inst_index].pl_node;
1308 const extra = file.zir.extraData(Zir.Inst.SliceLength, pl_node.payload_index);
1309
1310 const slice_index = self.exprs.items.len;
1311 try self.exprs.append(self.arena, .{ .slice = .{ .lhs = 0, .start = 0 } });
1312
1313 var lhs: DocData.WalkResult = try self.walkRef(
1314 file,
1315 parent_scope,
1316 parent_src,
1317 extra.data.lhs,
1318 false,
1319 );
1320 var start: DocData.WalkResult = try self.walkRef(
1321 file,
1322 parent_scope,
1323 parent_src,
1324 extra.data.start,
1325 false,
1326 );
1327 var len: DocData.WalkResult = try self.walkRef(
1328 file,
1329 parent_scope,
1330 parent_src,
1331 extra.data.len,
1332 false,
1333 );
1334 var sentinel_opt: ?DocData.WalkResult = if (extra.data.sentinel != .none)
1335 try self.walkRef(
1336 file,
1337 parent_scope,
1338 parent_src,
1339 extra.data.sentinel,
1340 false,
1341 )
1342 else
1343 null;
1344
1345 const lhs_index = self.exprs.items.len;
1346 try self.exprs.append(self.arena, lhs.expr);
1347 const start_index = self.exprs.items.len;
1348 try self.exprs.append(self.arena, start.expr);
1349 const len_index = self.exprs.items.len;
1350 try self.exprs.append(self.arena, len.expr);
1351 const sentinel_index = if (sentinel_opt) |sentinel| sentinel_index: {
1352 const index = self.exprs.items.len;
1353 try self.exprs.append(self.arena, sentinel.expr);
1354 break :sentinel_index index;
1355 } else null;
1356 self.exprs.items[slice_index] = .{ .sliceLength = .{
1357 .lhs = lhs_index,
1358 .start = start_index,
1359 .len = len_index,
1360 .sentinel = sentinel_index,
1361 } };
1362
1363 return DocData.WalkResult{
1364 .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef,
1365 .expr = .{ .sliceIndex = slice_index },
1366 };
1367 },
12991368
1300 // @check array_cat and array_mul1369 // @check array_cat and array_mul
1301 .add,1370 .add,
src/Sema.zig+48-9
...@@ -985,6 +985,7 @@ fn analyzeBodyInner(...@@ -985,6 +985,7 @@ fn analyzeBodyInner(
985 .slice_end => try sema.zirSliceEnd(block, inst),985 .slice_end => try sema.zirSliceEnd(block, inst),
986 .slice_sentinel => try sema.zirSliceSentinel(block, inst),986 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
987 .slice_start => try sema.zirSliceStart(block, inst),987 .slice_start => try sema.zirSliceStart(block, inst),
988 .slice_length => try sema.zirSliceLength(block, inst),
988 .str => try sema.zirStr(block, inst),989 .str => try sema.zirStr(block, inst),
989 .switch_block => try sema.zirSwitchBlock(block, inst),990 .switch_block => try sema.zirSwitchBlock(block, inst),
990 .switch_cond => try sema.zirSwitchCond(block, inst, false),991 .switch_cond => try sema.zirSwitchCond(block, inst, false),
...@@ -9931,7 +9932,7 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!...@@ -9931,7 +9932,7 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!
9931 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };9932 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
9932 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };9933 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
99339934
9934 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src);9935 return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src, false);
9935}9936}
99369937
9937fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9938fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -9948,7 +9949,7 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai...@@ -9948,7 +9949,7 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai
9948 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };9949 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
9949 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };9950 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
99509951
9951 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src);9952 return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src, false);
9952}9953}
99539954
9954fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {9955fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
...@@ -9967,7 +9968,29 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr...@@ -9967,7 +9968,29 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr
9967 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };9968 const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node };
9968 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };9969 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
99699970
9970 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src);9971 return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false);
9972}
9973
9974fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
9975 const tracy = trace(@src());
9976 defer tracy.end();
9977
9978 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
9979 const src = inst_data.src();
9980 const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
9981 const array_ptr = try sema.resolveInst(extra.lhs);
9982 const start = try sema.resolveInst(extra.start);
9983 const len = try sema.resolveInst(extra.len);
9984 const sentinel = try sema.resolveInst(extra.sentinel);
9985 const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node };
9986 const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset };
9987 const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node };
9988 const sentinel_src: LazySrcLoc = if (sentinel == .none)
9989 .unneeded
9990 else
9991 .{ .node_offset_slice_sentinel = inst_data.src_node };
9992
9993 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
9971}9994}
99729995
9973fn zirSwitchCapture(9996fn zirSwitchCapture(
...@@ -29193,6 +29216,7 @@ fn analyzeSlice(...@@ -29193,6 +29216,7 @@ fn analyzeSlice(
29193 ptr_src: LazySrcLoc,29216 ptr_src: LazySrcLoc,
29194 start_src: LazySrcLoc,29217 start_src: LazySrcLoc,
29195 end_src: LazySrcLoc,29218 end_src: LazySrcLoc,
29219 by_length: bool,
29196) CompileError!Air.Inst.Ref {29220) CompileError!Air.Inst.Ref {
29197 // Slice expressions can operate on a variable whose type is an array. This requires29221 // Slice expressions can operate on a variable whose type is an array. This requires
29198 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.29222 // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer.
...@@ -29271,7 +29295,11 @@ fn analyzeSlice(...@@ -29271,7 +29295,11 @@ fn analyzeSlice(
29271 const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen());29295 const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen());
2927229296
29273 if (!end_is_len) {29297 if (!end_is_len) {
29274 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);29298 const end = if (by_length) end: {
29299 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29300 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
29301 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
29302 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29275 if (try sema.resolveMaybeUndefVal(end)) |end_val| {29303 if (try sema.resolveMaybeUndefVal(end)) |end_val| {
29276 const len_s_val = try Value.Tag.int_u64.create(29304 const len_s_val = try Value.Tag.int_u64.create(
29277 sema.arena,29305 sema.arena,
...@@ -29308,7 +29336,11 @@ fn analyzeSlice(...@@ -29308,7 +29336,11 @@ fn analyzeSlice(
29308 break :e try sema.addConstant(Type.usize, len_val);29336 break :e try sema.addConstant(Type.usize, len_val);
29309 } else if (slice_ty.isSlice()) {29337 } else if (slice_ty.isSlice()) {
29310 if (!end_is_len) {29338 if (!end_is_len) {
29311 const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);29339 const end = if (by_length) end: {
29340 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29341 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
29342 break :end try sema.coerce(block, Type.usize, uncasted_end, end_src);
29343 } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29312 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {29344 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
29313 if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| {29345 if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| {
29314 if (slice_val.isUndef()) {29346 if (slice_val.isUndef()) {
...@@ -29355,7 +29387,11 @@ fn analyzeSlice(...@@ -29355,7 +29387,11 @@ fn analyzeSlice(
29355 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);29387 break :e try sema.analyzeSliceLen(block, src, ptr_or_slice);
29356 }29388 }
29357 if (!end_is_len) {29389 if (!end_is_len) {
29358 break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);29390 if (by_length) {
29391 const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29392 const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false);
29393 break :e try sema.coerce(block, Type.usize, uncasted_end, end_src);
29394 } else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src);
29359 }29395 }
29360 return sema.fail(block, src, "slice of pointer must include end value", .{});29396 return sema.fail(block, src, "slice of pointer must include end value", .{});
29361 };29397 };
...@@ -29379,7 +29415,7 @@ fn analyzeSlice(...@@ -29379,7 +29415,7 @@ fn analyzeSlice(
29379 // requirement: start <= end29415 // requirement: start <= end
29380 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {29416 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
29381 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {29417 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
29382 if (!(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {29418 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
29383 return sema.fail(29419 return sema.fail(
29384 block,29420 block,
29385 start_src,29421 start_src,
...@@ -29432,11 +29468,14 @@ fn analyzeSlice(...@@ -29432,11 +29468,14 @@ fn analyzeSlice(
29432 }29468 }
29433 }29469 }
2943429470
29435 if (block.wantSafety() and !block.is_comptime) {29471 if (!by_length and block.wantSafety() and !block.is_comptime) {
29436 // requirement: start <= end29472 // requirement: start <= end
29437 try sema.panicStartLargerThanEnd(block, start, end);29473 try sema.panicStartLargerThanEnd(block, start, end);
29438 }29474 }
29439 const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);29475 const new_len = if (by_length)
29476 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
29477 else
29478 try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false);
29440 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);29479 const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len);
2944129480
29442 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;29481 const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data;
src/Zir.zig+15
...@@ -570,6 +570,10 @@ pub const Inst = struct {...@@ -570,6 +570,10 @@ pub const Inst = struct {
570 /// Returns a pointer to the subslice.570 /// Returns a pointer to the subslice.
571 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.571 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`.
572 slice_sentinel,572 slice_sentinel,
573 /// Slice operation `array_ptr[start..][0..len]`. Optional sentinel.
574 /// Returns a pointer to the subslice.
575 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
576 slice_length,
573 /// Write a value to a pointer. For loading, see `load`.577 /// Write a value to a pointer. For loading, see `load`.
574 /// Source location is assumed to be same as previous instruction.578 /// Source location is assumed to be same as previous instruction.
575 /// Uses the `bin` union field.579 /// Uses the `bin` union field.
...@@ -1135,6 +1139,7 @@ pub const Inst = struct {...@@ -1135,6 +1139,7 @@ pub const Inst = struct {
1135 .slice_start,1139 .slice_start,
1136 .slice_end,1140 .slice_end,
1137 .slice_sentinel,1141 .slice_sentinel,
1142 .slice_length,
1138 .import,1143 .import,
1139 .typeof_log2_int_type,1144 .typeof_log2_int_type,
1140 .resolve_inferred_alloc,1145 .resolve_inferred_alloc,
...@@ -1430,6 +1435,7 @@ pub const Inst = struct {...@@ -1430,6 +1435,7 @@ pub const Inst = struct {
1430 .slice_start,1435 .slice_start,
1431 .slice_end,1436 .slice_end,
1432 .slice_sentinel,1437 .slice_sentinel,
1438 .slice_length,
1433 .import,1439 .import,
1434 .typeof_log2_int_type,1440 .typeof_log2_int_type,
1435 .switch_capture,1441 .switch_capture,
...@@ -1667,6 +1673,7 @@ pub const Inst = struct {...@@ -1667,6 +1673,7 @@ pub const Inst = struct {
1667 .slice_start = .pl_node,1673 .slice_start = .pl_node,
1668 .slice_end = .pl_node,1674 .slice_end = .pl_node,
1669 .slice_sentinel = .pl_node,1675 .slice_sentinel = .pl_node,
1676 .slice_length = .pl_node,
1670 .store = .bin,1677 .store = .bin,
1671 .store_node = .pl_node,1678 .store_node = .pl_node,
1672 .store_to_block_ptr = .bin,1679 .store_to_block_ptr = .bin,
...@@ -2980,6 +2987,14 @@ pub const Inst = struct {...@@ -2980,6 +2987,14 @@ pub const Inst = struct {
2980 sentinel: Ref,2987 sentinel: Ref,
2981 };2988 };
29822989
2990 pub const SliceLength = struct {
2991 lhs: Ref,
2992 start: Ref,
2993 len: Ref,
2994 sentinel: Ref,
2995 start_src_node_offset: i32,
2996 };
2997
2983 /// The meaning of these operands depends on the corresponding `Tag`.2998 /// The meaning of these operands depends on the corresponding `Tag`.
2984 pub const Bin = struct {2999 pub const Bin = struct {
2985 lhs: Ref,3000 lhs: Ref,
src/link/Plan9/aout.zig+1-1
...@@ -21,7 +21,7 @@ pub const ExecHdr = extern struct {...@@ -21,7 +21,7 @@ pub const ExecHdr = extern struct {
21 var buf: [40]u8 = undefined;21 var buf: [40]u8 = undefined;
22 var i: u8 = 0;22 var i: u8 = 0;
23 inline for (std.meta.fields(@This())) |f| {23 inline for (std.meta.fields(@This())) |f| {
24 std.mem.writeIntSliceBig(u32, buf[i .. i + 4], @field(self, f.name));24 std.mem.writeIntSliceBig(u32, buf[i..][0..4], @field(self, f.name));
25 i += 4;25 i += 4;
26 }26 }
27 return buf;27 return buf;
src/print_zir.zig+17
...@@ -267,6 +267,7 @@ const Writer = struct {...@@ -267,6 +267,7 @@ const Writer = struct {
267 .slice_start => try self.writeSliceStart(stream, inst),267 .slice_start => try self.writeSliceStart(stream, inst),
268 .slice_end => try self.writeSliceEnd(stream, inst),268 .slice_end => try self.writeSliceEnd(stream, inst),
269 .slice_sentinel => try self.writeSliceSentinel(stream, inst),269 .slice_sentinel => try self.writeSliceSentinel(stream, inst),
270 .slice_length => try self.writeSliceLength(stream, inst),
270271
271 .union_init => try self.writeUnionInit(stream, inst),272 .union_init => try self.writeUnionInit(stream, inst),
272273
...@@ -756,6 +757,22 @@ const Writer = struct {...@@ -756,6 +757,22 @@ const Writer = struct {
756 try self.writeSrc(stream, inst_data.src());757 try self.writeSrc(stream, inst_data.src());
757 }758 }
758759
760 fn writeSliceLength(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
761 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
762 const extra = self.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data;
763 try self.writeInstRef(stream, extra.lhs);
764 try stream.writeAll(", ");
765 try self.writeInstRef(stream, extra.start);
766 try stream.writeAll(", ");
767 try self.writeInstRef(stream, extra.len);
768 if (extra.sentinel != .none) {
769 try stream.writeAll(", ");
770 try self.writeInstRef(stream, extra.sentinel);
771 }
772 try stream.writeAll(") ");
773 try self.writeSrc(stream, inst_data.src());
774 }
775
759 fn writeUnionInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {776 fn writeUnionInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void {
760 const inst_data = self.code.instructions.items(.data)[inst].pl_node;777 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
761 const extra = self.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;778 const extra = self.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data;
test/behavior/slice.zig+77
...@@ -180,6 +180,18 @@ test "slicing zero length array" {...@@ -180,6 +180,18 @@ test "slicing zero length array" {
180 try expect(mem.eql(u32, s2, &[_]u32{}));180 try expect(mem.eql(u32, s2, &[_]u32{}));
181}181}
182182
183test "slicing pointer by length" {
184 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
185 const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 };
186 const ptr: [*]const u8 = @ptrCast([*]const u8, &array);
187 const slice = ptr[1..][0..5];
188 try expect(slice.len == 5);
189 var i: usize = 0;
190 while (i < slice.len) : (i += 1) {
191 try expect(slice[i] == i + 2);
192 }
193}
194
183const x = @intToPtr([*]i32, 0x1000)[0..0x500];195const x = @intToPtr([*]i32, 0x1000)[0..0x500];
184const y = x[0x100..];196const y = x[0x100..];
185test "compile time slice of pointer to hard coded address" {197test "compile time slice of pointer to hard coded address" {
...@@ -355,6 +367,10 @@ test "slice syntax resulting in pointer-to-array" {...@@ -355,6 +367,10 @@ test "slice syntax resulting in pointer-to-array" {
355 try testSlice();367 try testSlice();
356 try testSliceOpt();368 try testSliceOpt();
357 try testSliceAlign();369 try testSliceAlign();
370 try testSliceLength();
371 try testSliceLengthZ();
372 try testArrayLength();
373 try testArrayLengthZ();
358 }374 }
359375
360 fn testArray() !void {376 fn testArray() !void {
...@@ -465,6 +481,67 @@ test "slice syntax resulting in pointer-to-array" {...@@ -465,6 +481,67 @@ test "slice syntax resulting in pointer-to-array" {
465 try expectEqualSlices("a"[0..] ++ "b"[0..], "ab");481 try expectEqualSlices("a"[0..] ++ "b"[0..], "ab");
466 try expectEqualSlices("a"[0.. :0] ++ "b"[0.. :0], "ab");482 try expectEqualSlices("a"[0.. :0] ++ "b"[0.. :0], "ab");
467 }483 }
484
485 fn testSliceLength() !void {
486 var array = [5]u8{ 1, 2, 3, 4, 5 };
487 var slice: []u8 = &array;
488 comptime try expect(@TypeOf(slice[1..][0..2]) == *[2]u8);
489 comptime try expect(@TypeOf(slice[1..][0..4]) == *[4]u8);
490 comptime try expect(@TypeOf(slice[1..][0..2 :4]) == *[2:4]u8);
491 }
492
493 fn testSliceLengthZ() !void {
494 var array = [5:0]u8{ 1, 2, 3, 4, 5 };
495 var slice: [:0]u8 = &array;
496 comptime try expect(@TypeOf(slice[1..][0..2]) == *[2]u8);
497 comptime try expect(@TypeOf(slice[1..][0..2 :4]) == *[2:4]u8);
498 comptime try expect(@TypeOf(slice[1.. :0][0..2]) == *[2]u8);
499 comptime try expect(@TypeOf(slice[1.. :0][0..2 :4]) == *[2:4]u8);
500 }
501
502 fn testArrayLength() !void {
503 var array = [5]u8{ 1, 2, 3, 4, 5 };
504 comptime try expect(@TypeOf(array[1..][0..2]) == *[2]u8);
505 comptime try expect(@TypeOf(array[1..][0..4]) == *[4]u8);
506 comptime try expect(@TypeOf(array[1..][0..2 :4]) == *[2:4]u8);
507 }
508
509 fn testArrayLengthZ() !void {
510 var array = [5:0]u8{ 1, 2, 3, 4, 5 };
511 comptime try expect(@TypeOf(array[1..][0..2]) == *[2]u8);
512 comptime try expect(@TypeOf(array[1..][0..4]) == *[4:0]u8);
513 comptime try expect(@TypeOf(array[1..][0..2 :4]) == *[2:4]u8);
514 comptime try expect(@TypeOf(array[1.. :0][0..2]) == *[2]u8);
515 comptime try expect(@TypeOf(array[1.. :0][0..4]) == *[4:0]u8);
516 comptime try expect(@TypeOf(array[1.. :0][0..2 :4]) == *[2:4]u8);
517 }
518
519 fn testMultiPointer() !void {
520 var array = [5]u8{ 1, 2, 3, 4, 5 };
521 var ptr: [*]u8 = &array;
522 comptime try expect(@TypeOf(ptr[1..][0..2]) == *[2]u8);
523 comptime try expect(@TypeOf(ptr[1..][0..4]) == *[4]u8);
524 comptime try expect(@TypeOf(ptr[1..][0..2 :4]) == *[2:4]u8);
525 }
526
527 fn testMultiPointerLengthZ() !void {
528 var array = [5:0]u8{ 1, 2, 3, 4, 5 };
529 var ptr: [*]u8 = &array;
530 comptime try expect(@TypeOf(ptr[1..][0..2]) == *[2]u8);
531 comptime try expect(@TypeOf(ptr[1..][0..4]) == *[4:0]u8);
532 comptime try expect(@TypeOf(ptr[1..][0..2 :4]) == *[2:4]u8);
533 comptime try expect(@TypeOf(ptr[1.. :0][0..2]) == *[2]u8);
534 comptime try expect(@TypeOf(ptr[1.. :0][0..4]) == *[4:0]u8);
535 comptime try expect(@TypeOf(ptr[1.. :0][0..2 :4]) == *[2:4]u8);
536
537 var ptr_z: [*:0]u8 = &array;
538 comptime try expect(@TypeOf(ptr_z[1..][0..2]) == *[2]u8);
539 comptime try expect(@TypeOf(ptr_z[1..][0..4]) == *[4:0]u8);
540 comptime try expect(@TypeOf(ptr_z[1..][0..2 :4]) == *[2:4]u8);
541 comptime try expect(@TypeOf(ptr_z[1.. :0][0..2]) == *[2]u8);
542 comptime try expect(@TypeOf(ptr_z[1.. :0][0..4]) == *[4:0]u8);
543 comptime try expect(@TypeOf(ptr_z[1.. :0][0..2 :4]) == *[2:4]u8);
544 }
468 };545 };
469546
470 try S.doTheTest();547 try S.doTheTest();