| 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 | 2953 | const array_ptr = array[0..array.len]; |
| 2954 | 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 | 2964 | // Using the address-of operator on a slice gives a single-item pointer, |
| 2957 | 2965 | // while using the `ptr` field gives a many-item pointer. |
| 2958 | 2966 | try expect(@TypeOf(slice.ptr) == [*]i32); |
lib/docs/main.js+17| ... | ... | @@ -1097,6 +1097,23 @@ const NAV_MODES = { |
| 1097 | 1097 | payloadHtml += decl + "[" + start + ".." + end + sentinel + "]"; |
| 1098 | 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 | 1117 | case "sliceIndex": { |
| 1101 | 1118 | const sliceIndex = zigAnalysis.exprs[expr.sliceIndex]; |
| 1102 | 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 | 974 | var line: usize = 0; |
| 975 | 975 | var offset: usize = 0; |
| 976 | 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 | 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 | 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 | 177 | /// Grows list if `len < new_items.len`. |
| 178 | 178 | /// Shrinks list if `len > new_items.len`. |
| 179 | 179 | /// Invalidates pointers if this ArrayList is resized. |
| ... | ... | @@ -654,7 +654,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ |
| 654 | 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 | 658 | /// Grows list if `len < new_items.len`. |
| 659 | 659 | /// Shrinks list if `len > new_items.len` |
| 660 | 660 | /// Invalidates pointers if this ArrayList is resized. |
lib/std/bounded_array.zig+1-1| ... | ... | @@ -168,7 +168,7 @@ pub fn BoundedArrayAligned( |
| 168 | 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 | 172 | /// Grows slice if `len < new_items.len`. |
| 173 | 173 | /// Shrinks slice if `len > new_items.len`. |
| 174 | 174 | pub fn replaceRange( |
lib/std/compress/deflate/decompressor.zig+1-1| ... | ... | @@ -591,7 +591,7 @@ pub fn Decompressor(comptime ReaderType: type) type { |
| 591 | 591 | } |
| 592 | 592 | |
| 593 | 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 | 596 | corrupt_input_error_offset = self.roffset; |
| 597 | 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 | 139 | self.bits >>= 48; |
| 140 | 140 | self.nbits -= 48; |
| 141 | 141 | var n = self.nbytes; |
| 142 | var bytes = self.bytes[n .. n + 6]; | |
| 142 | var bytes = self.bytes[n..][0..6]; | |
| 143 | 143 | bytes[0] = @truncate(u8, bits); |
| 144 | 144 | bytes[1] = @truncate(u8, bits >> 8); |
| 145 | 145 | bytes[2] = @truncate(u8, bits >> 16); |
| ... | ... | @@ -344,7 +344,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type { |
| 344 | 344 | self.bits >>= 48; |
| 345 | 345 | self.nbits -= 48; |
| 346 | 346 | var n = self.nbytes; |
| 347 | var bytes = self.bytes[n .. n + 6]; | |
| 347 | var bytes = self.bytes[n..][0..6]; | |
| 348 | 348 | bytes[0] = @truncate(u8, bits); |
| 349 | 349 | bytes[1] = @truncate(u8, bits >> 8); |
| 350 | 350 | bytes[2] = @truncate(u8, bits >> 16); |
| ... | ... | @@ -751,7 +751,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type { |
| 751 | 751 | var bits = self.bits; |
| 752 | 752 | self.bits >>= 48; |
| 753 | 753 | self.nbits -= 48; |
| 754 | var bytes = self.bytes[n .. n + 6]; | |
| 754 | var bytes = self.bytes[n..][0..6]; | |
| 755 | 755 | bytes[0] = @truncate(u8, bits); |
| 756 | 756 | bytes[1] = @truncate(u8, bits >> 8); |
| 757 | 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 | 160 | pub fn update(self: *Self, input: []const u8) void { |
| 161 | 161 | var i: usize = 0; |
| 162 | 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 | 165 | // Unrolling this way gives ~50Mb/s increase |
| 166 | 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 | 65 | |
| 66 | 66 | var off: usize = 0; |
| 67 | 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 | 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 | 63 | var buf: [4]u8 = undefined; |
| 64 | 64 | const len = std.unicode.utf8Encode(codepoint, &buf) catch unreachable; |
| 65 | 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 | 67 | i += len; |
| 68 | 68 | } |
| 69 | 69 | } |
| ... | ... | @@ -2285,10 +2285,10 @@ pub fn encodeJsonStringChars(chars: []const u8, options: StringifyOptions, write |
| 2285 | 2285 | const ulen = std.unicode.utf8ByteSequenceLength(chars[i]) catch unreachable; |
| 2286 | 2286 | // control characters (only things left with 1 byte length) should always be printed as unicode escapes |
| 2287 | 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 | 2289 | try outputUnicodeEscape(codepoint, writer); |
| 2290 | 2290 | } else { |
| 2291 | try writer.writeAll(chars[i .. i + ulen]); | |
| 2291 | try writer.writeAll(chars[i..][0..ulen]); | |
| 2292 | 2292 | } |
| 2293 | 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 | 4035 | |
| 4036 | 4036 | for (x_norm, 0..) |v, i| { |
| 4037 | 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 | 4039 | assert(!overflow); |
| 4040 | 4040 | } |
| 4041 | 4041 | } |
lib/std/net.zig+1-1| ... | ... | @@ -1701,7 +1701,7 @@ fn dnsParse( |
| 1701 | 1701 | p += @as(usize, 1) + @boolToInt(p[0] != 0); |
| 1702 | 1702 | const len = p[8] * @as(usize, 256) + p[9]; |
| 1703 | 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 | 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 | 835 | const len = buf.SubstituteNameLength >> 1; |
| 836 | 836 | const path_buf = @as([*]const u16, &buf.PathBuffer); |
| 837 | 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 | 840 | IO_REPARSE_TAG_MOUNT_POINT => { |
| 841 | 841 | const buf = @ptrCast(*const MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0])); |
| 842 | 842 | const offset = buf.SubstituteNameOffset >> 1; |
| 843 | 843 | const len = buf.SubstituteNameLength >> 1; |
| 844 | 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 | 847 | else => |value| { |
| 848 | 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 | 941 | fn printWithVisibleNewlines(source: []const u8) void { |
| 942 | 942 | var i: usize = 0; |
| 943 | 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 | 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 | 185 | |
| 186 | 186 | switch (n) { |
| 187 | 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 | 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 | 849 | return rvalue(gz, ri, result, node); |
| 850 | 850 | }, |
| 851 | 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 | 878 | const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); |
| 853 | 879 | |
| 854 | 880 | const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); |
| 855 | const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); | |
| 856 | 881 | const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); |
| 857 | 882 | const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end); |
| 858 | 883 | try emitDbgStmt(gz, cursor); |
| ... | ... | @@ -864,10 +889,36 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE |
| 864 | 889 | return rvalue(gz, ri, result, node); |
| 865 | 890 | }, |
| 866 | 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 | 919 | const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); |
| 868 | 920 | |
| 869 | 921 | const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); |
| 870 | const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); | |
| 871 | 922 | const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); |
| 872 | 923 | const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; |
| 873 | 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 | 2608 | .slice_start, |
| 2558 | 2609 | .slice_end, |
| 2559 | 2610 | .slice_sentinel, |
| 2611 | .slice_length, | |
| 2560 | 2612 | .import, |
| 2561 | 2613 | .switch_block, |
| 2562 | 2614 | .switch_cond, |
src/Autodoc.zig+69| ... | ... | @@ -758,6 +758,7 @@ const DocData = struct { |
| 758 | 758 | string: []const u8, // direct value |
| 759 | 759 | sliceIndex: usize, |
| 760 | 760 | slice: Slice, |
| 761 | sliceLength: SliceLength, | |
| 761 | 762 | cmpxchgIndex: usize, |
| 762 | 763 | cmpxchg: Cmpxchg, |
| 763 | 764 | builtin: Builtin, |
| ... | ... | @@ -794,6 +795,12 @@ const DocData = struct { |
| 794 | 795 | end: ?usize = null, |
| 795 | 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 | 804 | const Cmpxchg = struct { |
| 798 | 805 | name: []const u8, |
| 799 | 806 | type: usize, |
| ... | ... | @@ -1296,6 +1303,68 @@ fn walkInstruction( |
| 1296 | 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 | 1369 | // @check array_cat and array_mul |
| 1301 | 1370 | .add, |
src/Sema.zig+48-9| ... | ... | @@ -985,6 +985,7 @@ fn analyzeBodyInner( |
| 985 | 985 | .slice_end => try sema.zirSliceEnd(block, inst), |
| 986 | 986 | .slice_sentinel => try sema.zirSliceSentinel(block, inst), |
| 987 | 987 | .slice_start => try sema.zirSliceStart(block, inst), |
| 988 | .slice_length => try sema.zirSliceLength(block, inst), | |
| 988 | 989 | .str => try sema.zirStr(block, inst), |
| 989 | 990 | .switch_block => try sema.zirSwitchBlock(block, inst), |
| 990 | 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 | 9932 | const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; |
| 9932 | 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 | 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 | 9949 | const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; |
| 9949 | 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 | 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 | 9968 | const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; |
| 9968 | 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 | 9996 | fn zirSwitchCapture( |
| ... | ... | @@ -29193,6 +29216,7 @@ fn analyzeSlice( |
| 29193 | 29216 | ptr_src: LazySrcLoc, |
| 29194 | 29217 | start_src: LazySrcLoc, |
| 29195 | 29218 | end_src: LazySrcLoc, |
| 29219 | by_length: bool, | |
| 29196 | 29220 | ) CompileError!Air.Inst.Ref { |
| 29197 | 29221 | // Slice expressions can operate on a variable whose type is an array. This requires |
| 29198 | 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 | 29295 | const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()); |
| 29272 | 29296 | |
| 29273 | 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 | 29303 | if (try sema.resolveMaybeUndefVal(end)) |end_val| { |
| 29276 | 29304 | const len_s_val = try Value.Tag.int_u64.create( |
| 29277 | 29305 | sema.arena, |
| ... | ... | @@ -29308,7 +29336,11 @@ fn analyzeSlice( |
| 29308 | 29336 | break :e try sema.addConstant(Type.usize, len_val); |
| 29309 | 29337 | } else if (slice_ty.isSlice()) { |
| 29310 | 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 | 29344 | if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { |
| 29313 | 29345 | if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| { |
| 29314 | 29346 | if (slice_val.isUndef()) { |
| ... | ... | @@ -29355,7 +29387,11 @@ fn analyzeSlice( |
| 29355 | 29387 | break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); |
| 29356 | 29388 | } |
| 29357 | 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 | 29396 | return sema.fail(block, src, "slice of pointer must include end value", .{}); |
| 29361 | 29397 | }; |
| ... | ... | @@ -29379,7 +29415,7 @@ fn analyzeSlice( |
| 29379 | 29415 | // requirement: start <= end |
| 29380 | 29416 | if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { |
| 29381 | 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 | 29419 | return sema.fail( |
| 29384 | 29420 | block, |
| 29385 | 29421 | start_src, |
| ... | ... | @@ -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 | 29472 | // requirement: start <= end |
| 29437 | 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 | 29479 | const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); |
| 29441 | 29480 | |
| 29442 | 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 | 570 | /// Returns a pointer to the subslice. |
| 571 | 571 | /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`. |
| 572 | 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 | 577 | /// Write a value to a pointer. For loading, see `load`. |
| 574 | 578 | /// Source location is assumed to be same as previous instruction. |
| 575 | 579 | /// Uses the `bin` union field. |
| ... | ... | @@ -1135,6 +1139,7 @@ pub const Inst = struct { |
| 1135 | 1139 | .slice_start, |
| 1136 | 1140 | .slice_end, |
| 1137 | 1141 | .slice_sentinel, |
| 1142 | .slice_length, | |
| 1138 | 1143 | .import, |
| 1139 | 1144 | .typeof_log2_int_type, |
| 1140 | 1145 | .resolve_inferred_alloc, |
| ... | ... | @@ -1430,6 +1435,7 @@ pub const Inst = struct { |
| 1430 | 1435 | .slice_start, |
| 1431 | 1436 | .slice_end, |
| 1432 | 1437 | .slice_sentinel, |
| 1438 | .slice_length, | |
| 1433 | 1439 | .import, |
| 1434 | 1440 | .typeof_log2_int_type, |
| 1435 | 1441 | .switch_capture, |
| ... | ... | @@ -1667,6 +1673,7 @@ pub const Inst = struct { |
| 1667 | 1673 | .slice_start = .pl_node, |
| 1668 | 1674 | .slice_end = .pl_node, |
| 1669 | 1675 | .slice_sentinel = .pl_node, |
| 1676 | .slice_length = .pl_node, | |
| 1670 | 1677 | .store = .bin, |
| 1671 | 1678 | .store_node = .pl_node, |
| 1672 | 1679 | .store_to_block_ptr = .bin, |
| ... | ... | @@ -2980,6 +2987,14 @@ pub const Inst = struct { |
| 2980 | 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 | 2998 | /// The meaning of these operands depends on the corresponding `Tag`. |
| 2984 | 2999 | pub const Bin = struct { |
| 2985 | 3000 | lhs: Ref, |
src/link/Plan9/aout.zig+1-1| ... | ... | @@ -21,7 +21,7 @@ pub const ExecHdr = extern struct { |
| 21 | 21 | var buf: [40]u8 = undefined; |
| 22 | 22 | var i: u8 = 0; |
| 23 | 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 | 25 | i += 4; |
| 26 | 26 | } |
| 27 | 27 | return buf; |
src/print_zir.zig+17| ... | ... | @@ -267,6 +267,7 @@ const Writer = struct { |
| 267 | 267 | .slice_start => try self.writeSliceStart(stream, inst), |
| 268 | 268 | .slice_end => try self.writeSliceEnd(stream, inst), |
| 269 | 269 | .slice_sentinel => try self.writeSliceSentinel(stream, inst), |
| 270 | .slice_length => try self.writeSliceLength(stream, inst), | |
| 270 | 271 | |
| 271 | 272 | .union_init => try self.writeUnionInit(stream, inst), |
| 272 | 273 | |
| ... | ... | @@ -756,6 +757,22 @@ const Writer = struct { |
| 756 | 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 | 776 | fn writeUnionInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { |
| 760 | 777 | const inst_data = self.code.instructions.items(.data)[inst].pl_node; |
| 761 | 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 | 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 | 195 | const x = @intToPtr([*]i32, 0x1000)[0..0x500]; |
| 184 | 196 | const y = x[0x100..]; |
| 185 | 197 | test "compile time slice of pointer to hard coded address" { |
| ... | ... | @@ -355,6 +367,10 @@ test "slice syntax resulting in pointer-to-array" { |
| 355 | 367 | try testSlice(); |
| 356 | 368 | try testSliceOpt(); |
| 357 | 369 | try testSliceAlign(); |
| 370 | try testSliceLength(); | |
| 371 | try testSliceLengthZ(); | |
| 372 | try testArrayLength(); | |
| 373 | try testArrayLengthZ(); | |
| 358 | 374 | } |
| 359 | 375 | |
| 360 | 376 | fn testArray() !void { |
| ... | ... | @@ -465,6 +481,67 @@ test "slice syntax resulting in pointer-to-array" { |
| 465 | 481 | try expectEqualSlices("a"[0..] ++ "b"[0..], "ab"); |
| 466 | 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 | 547 | try S.doTheTest(); |