| author | |
| committer | |
| log | 7f7bd206dc8309ca767b47fb97bb9e7c2dc882c3 |
| tree | 2d24acb80954ec2d33863befa9ce48ecd3075e72 |
| parent | 5512455974a9dda5d2a86a81e4a5cc520cb7afa8 |
| parent | 4d296debefccbd80f2007685a27386b7434464dd |
| signature |
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); |
| 2955 | 2955 | ||
| 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 | } |
| 980 | 980 |
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 | } |
| 175 | 175 | ||
| 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 | } |
| 656 | 656 | ||
| 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 | } |
| 170 | 170 | ||
| 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 | } |
| 592 | 592 | ||
| 593 | if (!try self.hd1.init(self.allocator, self.bits[0..nlit]) or | 593 | 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]; |
| 164 | 164 | ||
| 165 | // Unrolling this way gives ~50Mb/s increase | 165 | // 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 { |
| 65 | 65 | ||
| 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 | } |
| 70 | 70 | ||
| 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 escapes | 2286 | // 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 { |
| 4035 | 4035 | ||
| 4036 | for (x_norm, 0..) |v, i| { | 4036 | for (x_norm, 0..) |v, i| { |
| 4037 | // Compute and add the squares | 4037 | // 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 { |
| 941 | fn printWithVisibleNewlines(source: []const u8) void { | 941 | fn 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 { |
| 185 | 185 | ||
| 186 | switch (n) { | 186 | switch (n) { |
| 187 | 1 => {}, // ASCII, no validation needed | 187 | 1 => {}, // ASCII, no validation needed |
| 188 | else => _ = try utf8Decode(s[i .. i + n]), | 188 | else => _ = try utf8Decode(s[i..][0..n]), |
| 189 | } | 189 | } |
| 190 | 190 | ||
| 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); |
| 853 | 879 | ||
| 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); |
| 868 | 920 | ||
| 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 value | 758 | 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 | }, | ||
| 1299 | 1368 | ||
| 1300 | // @check array_cat and array_mul | 1369 | // @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 }; |
| 9933 | 9934 | ||
| 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 | } |
| 9936 | 9937 | ||
| 9937 | fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 9938 | fn 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 }; |
| 9950 | 9951 | ||
| 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 | } |
| 9953 | 9954 | ||
| 9954 | fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 9955 | fn 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 }; |
| 9969 | 9970 | ||
| 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 | |||
| 9974 | fn 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 | } |
| 9972 | 9995 | ||
| 9973 | fn zirSwitchCapture( | 9996 | fn 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 requires | 29221 | // 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()); |
| 29272 | 29296 | ||
| 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 <= end | 29415 | // 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 | } |
| 29434 | 29470 | ||
| 29435 | if (block.wantSafety() and !block.is_comptime) { | 29471 | if (!by_length and block.wantSafety() and !block.is_comptime) { |
| 29436 | // requirement: start <= end | 29472 | // 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); |
| 29441 | 29480 | ||
| 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 | }; |
| 2982 | 2989 | ||
| 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), | ||
| 270 | 271 | ||
| 271 | .union_init => try self.writeUnionInit(stream, inst), | 272 | .union_init => try self.writeUnionInit(stream, inst), |
| 272 | 273 | ||
| ... | @@ -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 | } |
| 758 | 759 | ||
| 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 | } |
| 182 | 182 | ||
| 183 | test "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 | |||
| 183 | const x = @intToPtr([*]i32, 0x1000)[0..0x500]; | 195 | const x = @intToPtr([*]i32, 0x1000)[0..0x500]; |
| 184 | const y = x[0x100..]; | 196 | const y = x[0x100..]; |
| 185 | test "compile time slice of pointer to hard coded address" { | 197 | test "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 | } |
| 359 | 375 | ||
| 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 | }; |
| 469 | 546 | ||
| 470 | try S.doTheTest(); | 547 | try S.doTheTest(); |