diff --git a/doc/langref.html.in b/doc/langref.html.in index 69ad0624c4e7..5876fd79ba70 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -2953,6 +2953,14 @@ test "basic slices" { const array_ptr = array[0..array.len]; try expect(@TypeOf(array_ptr) == *[array.len]i32); + // You can perform a slice-by-length by slicing twice. This allows the compiler + // to perform some optimisations like recognising a comptime-known length when + // the start position is only known at runtime. + var runtime_start: usize = 1; + const length = 2; + const array_ptr_len = array[runtime_start..][0..length]; + try expect(@TypeOf(array_ptr_len) == *[length]i32); + // Using the address-of operator on a slice gives a single-item pointer, // while using the `ptr` field gives a many-item pointer. try expect(@TypeOf(slice.ptr) == [*]i32); diff --git a/lib/docs/main.js b/lib/docs/main.js index 9b650643e9af..f803fddb813a 100644 --- a/lib/docs/main.js +++ b/lib/docs/main.js @@ -1093,6 +1093,23 @@ const NAV_MODES = { payloadHtml += decl + "[" + start + ".." + end + sentinel + "]"; return payloadHtml; } + case "sliceLength": { + let payloadHtml = ""; + const lhsExpr = zigAnalysis.exprs[expr.sliceLength.lhs]; + const startExpr = zigAnalysis.exprs[expr.sliceLength.start]; + const lenExpr = zigAnalysis.exprs[expr.sliceLength.len]; + let decl = exprName(lhsExpr, opts); + let start = exprName(startExpr, opts); + let len = exprName(lenExpr, opts); + let sentinel = ""; + if (expr.sliceLength["sentinel"]) { + const sentinelExpr = zigAnalysis.exprs[expr.sliceLength.sentinel]; + let sentinel_ = exprName(sentinelExpr, options); + sentinel += " :" + sentinel_; + } + payloadHtml += decl + "[" + start + "..][0.." + len + sentinel + "]"; + return payloadHtml; + } case "sliceIndex": { const sliceIndex = zigAnalysis.exprs[expr.sliceIndex]; return exprName(sliceIndex, opts, opts); @@ -4850,4 +4867,4 @@ function RadixTree() { // BUT! -// We want to be able to search "Hash", for example! \ No newline at end of file +// We want to be able to search "Hash", for example! diff --git a/lib/std/Build/Cache/DepTokenizer.zig b/lib/std/Build/Cache/DepTokenizer.zig index c640fa4adc49..1a4e2ddb74e5 100644 --- a/lib/std/Build/Cache/DepTokenizer.zig +++ b/lib/std/Build/Cache/DepTokenizer.zig @@ -974,7 +974,7 @@ fn hexDump(out: anytype, bytes: []const u8) !void { var line: usize = 0; var offset: usize = 0; while (line < n16) : (line += 1) { - try hexDump16(out, offset, bytes[offset .. offset + 16]); + try hexDump16(out, offset, bytes[offset..][0..16]); offset += 16; } diff --git a/lib/std/array_list.zig b/lib/std/array_list.zig index cbca601b8217..bbfa588d6dc7 100644 --- a/lib/std/array_list.zig +++ b/lib/std/array_list.zig @@ -173,7 +173,7 @@ pub fn ArrayListAligned(comptime T: type, comptime alignment: ?u29) type { @memcpy(self.items[i..][0..items.len], items); } - /// Replace range of elements `list[start..start+len]` with `new_items`. + /// Replace range of elements `list[start..][0..len]` with `new_items`. /// Grows list if `len < new_items.len`. /// Shrinks list if `len > new_items.len`. /// Invalidates pointers if this ArrayList is resized. @@ -654,7 +654,7 @@ pub fn ArrayListAlignedUnmanaged(comptime T: type, comptime alignment: ?u29) typ @memcpy(self.items[i..][0..items.len], items); } - /// Replace range of elements `list[start..start+len]` with `new_items` + /// Replace range of elements `list[start..][0..len]` with `new_items` /// Grows list if `len < new_items.len`. /// Shrinks list if `len > new_items.len` /// Invalidates pointers if this ArrayList is resized. diff --git a/lib/std/bounded_array.zig b/lib/std/bounded_array.zig index be32093fe7dd..0e0b601af68f 100644 --- a/lib/std/bounded_array.zig +++ b/lib/std/bounded_array.zig @@ -168,7 +168,7 @@ pub fn BoundedArrayAligned( @memcpy(self.slice()[i..][0..items.len], items); } - /// Replace range of elements `slice[start..start+len]` with `new_items`. + /// Replace range of elements `slice[start..][0..len]` with `new_items`. /// Grows slice if `len < new_items.len`. /// Shrinks slice if `len > new_items.len`. pub fn replaceRange( diff --git a/lib/std/compress/deflate/decompressor.zig b/lib/std/compress/deflate/decompressor.zig index 6c232c598eb7..40bde673263f 100644 --- a/lib/std/compress/deflate/decompressor.zig +++ b/lib/std/compress/deflate/decompressor.zig @@ -591,7 +591,7 @@ pub fn Decompressor(comptime ReaderType: type) type { } if (!try self.hd1.init(self.allocator, self.bits[0..nlit]) or - !try self.hd2.init(self.allocator, self.bits[nlit .. nlit + ndist])) + !try self.hd2.init(self.allocator, self.bits[nlit..][0..ndist])) { corrupt_input_error_offset = self.roffset; self.err = InflateError.CorruptInput; diff --git a/lib/std/compress/deflate/huffman_bit_writer.zig b/lib/std/compress/deflate/huffman_bit_writer.zig index a42aae467b84..0b76d9d89d2a 100644 --- a/lib/std/compress/deflate/huffman_bit_writer.zig +++ b/lib/std/compress/deflate/huffman_bit_writer.zig @@ -139,7 +139,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type { self.bits >>= 48; self.nbits -= 48; var n = self.nbytes; - var bytes = self.bytes[n .. n + 6]; + var bytes = self.bytes[n..][0..6]; bytes[0] = @truncate(u8, bits); bytes[1] = @truncate(u8, bits >> 8); bytes[2] = @truncate(u8, bits >> 16); @@ -344,7 +344,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type { self.bits >>= 48; self.nbits -= 48; var n = self.nbytes; - var bytes = self.bytes[n .. n + 6]; + var bytes = self.bytes[n..][0..6]; bytes[0] = @truncate(u8, bits); bytes[1] = @truncate(u8, bits >> 8); bytes[2] = @truncate(u8, bits >> 16); @@ -751,7 +751,7 @@ pub fn HuffmanBitWriter(comptime WriterType: type) type { var bits = self.bits; self.bits >>= 48; self.nbits -= 48; - var bytes = self.bytes[n .. n + 6]; + var bytes = self.bytes[n..][0..6]; bytes[0] = @truncate(u8, bits); bytes[1] = @truncate(u8, bits >> 8); bytes[2] = @truncate(u8, bits >> 16); diff --git a/lib/std/hash/crc.zig b/lib/std/hash/crc.zig index 271b4f93da92..0ad154abefd8 100644 --- a/lib/std/hash/crc.zig +++ b/lib/std/hash/crc.zig @@ -160,7 +160,7 @@ pub fn Crc32WithPoly(comptime poly: Polynomial) type { pub fn update(self: *Self, input: []const u8) void { var i: usize = 0; while (i + 8 <= input.len) : (i += 8) { - const p = input[i .. i + 8]; + const p = input[i..][0..8]; // Unrolling this way gives ~50Mb/s increase self.crc ^= std.mem.readIntLittle(u32, p[0..4]); diff --git a/lib/std/hash/wyhash.zig b/lib/std/hash/wyhash.zig index 772395eab97e..934c4f96adfa 100644 --- a/lib/std/hash/wyhash.zig +++ b/lib/std/hash/wyhash.zig @@ -65,7 +65,7 @@ const WyhashStateless = struct { var off: usize = 0; while (off < b.len) : (off += 32) { - @call(.always_inline, self.round, .{b[off .. off + 32]}); + @call(.always_inline, self.round, .{b[off..][0..32]}); } self.msg_len += b.len; diff --git a/lib/std/json.zig b/lib/std/json.zig index af928bc56425..011463faef98 100644 --- a/lib/std/json.zig +++ b/lib/std/json.zig @@ -63,7 +63,7 @@ fn encodesTo(decoded: []const u8, encoded: []const u8) bool { var buf: [4]u8 = undefined; const len = std.unicode.utf8Encode(codepoint, &buf) catch unreachable; if (i + len > decoded.len) return false; - if (!mem.eql(u8, decoded[i .. i + len], buf[0..len])) return false; + if (!mem.eql(u8, decoded[i..][0..len], buf[0..len])) return false; i += len; } } @@ -2285,10 +2285,10 @@ pub fn encodeJsonStringChars(chars: []const u8, options: StringifyOptions, write const ulen = std.unicode.utf8ByteSequenceLength(chars[i]) catch unreachable; // control characters (only things left with 1 byte length) should always be printed as unicode escapes if (ulen == 1 or options.string.String.escape_unicode) { - const codepoint = std.unicode.utf8Decode(chars[i .. i + ulen]) catch unreachable; + const codepoint = std.unicode.utf8Decode(chars[i..][0..ulen]) catch unreachable; try outputUnicodeEscape(codepoint, writer); } else { - try writer.writeAll(chars[i .. i + ulen]); + try writer.writeAll(chars[i..][0..ulen]); } i += ulen - 1; }, diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index b01d9b04ffe2..5683d8681cd0 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -4035,7 +4035,7 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void { for (x_norm, 0..) |v, i| { // Compute and add the squares - const overflow = llmulLimb(.add, r[2 * i ..], x[i .. i + 1], v); + const overflow = llmulLimb(.add, r[2 * i ..], x[i..][0..1], v); assert(!overflow); } } diff --git a/lib/std/net.zig b/lib/std/net.zig index 15a9e01da89a..57e50a7349f7 100644 --- a/lib/std/net.zig +++ b/lib/std/net.zig @@ -1701,7 +1701,7 @@ fn dnsParse( p += @as(usize, 1) + @boolToInt(p[0] != 0); const len = p[8] * @as(usize, 256) + p[9]; if (@ptrToInt(p) + len > @ptrToInt(r.ptr) + r.len) return error.InvalidDnsPacket; - try callback(ctx, p[1], p[10 .. 10 + len], r); + try callback(ctx, p[1], p[10..][0..len], r); p += 10 + len; } } diff --git a/lib/std/os/windows.zig b/lib/std/os/windows.zig index 22ffc850e6ea..6755e7adf28b 100644 --- a/lib/std/os/windows.zig +++ b/lib/std/os/windows.zig @@ -835,14 +835,14 @@ pub fn ReadLink(dir: ?HANDLE, sub_path_w: []const u16, out_buffer: []u8) ReadLin const len = buf.SubstituteNameLength >> 1; const path_buf = @as([*]const u16, &buf.PathBuffer); const is_relative = buf.Flags & SYMLINK_FLAG_RELATIVE != 0; - return parseReadlinkPath(path_buf[offset .. offset + len], is_relative, out_buffer); + return parseReadlinkPath(path_buf[offset..][0..len], is_relative, out_buffer); }, IO_REPARSE_TAG_MOUNT_POINT => { const buf = @ptrCast(*const MOUNT_POINT_REPARSE_BUFFER, @alignCast(@alignOf(MOUNT_POINT_REPARSE_BUFFER), &reparse_struct.DataBuffer[0])); const offset = buf.SubstituteNameOffset >> 1; const len = buf.SubstituteNameLength >> 1; const path_buf = @as([*]const u16, &buf.PathBuffer); - return parseReadlinkPath(path_buf[offset .. offset + len], false, out_buffer); + return parseReadlinkPath(path_buf[offset..][0..len], false, out_buffer); }, else => |value| { std.debug.print("unsupported symlink type: {}", .{value}); diff --git a/lib/std/testing.zig b/lib/std/testing.zig index 37e15ff08b26..2857ebdbd31b 100644 --- a/lib/std/testing.zig +++ b/lib/std/testing.zig @@ -941,7 +941,7 @@ fn printIndicatorLine(source: []const u8, indicator_index: usize) void { fn printWithVisibleNewlines(source: []const u8) void { var i: usize = 0; while (std.mem.indexOfScalar(u8, source[i..], '\n')) |nl| : (i += nl + 1) { - printLine(source[i .. i + nl]); + printLine(source[i..][0..nl]); } print("{s}␃\n", .{source[i..]}); // End of Text symbol (ETX) } diff --git a/lib/std/unicode.zig b/lib/std/unicode.zig index 378b216ef383..9d334218c18b 100644 --- a/lib/std/unicode.zig +++ b/lib/std/unicode.zig @@ -185,7 +185,7 @@ pub fn utf8CountCodepoints(s: []const u8) !usize { switch (n) { 1 => {}, // ASCII, no validation needed - else => _ = try utf8Decode(s[i .. i + n]), + else => _ = try utf8Decode(s[i..][0..n]), } i += n; diff --git a/src/AstGen.zig b/src/AstGen.zig index 749e3d28c461..8df8f0e1cf16 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -849,10 +849,35 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE return rvalue(gz, ri, result, node); }, .slice => { + const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); + const lhs_node = node_datas[node].lhs; + const lhs_tag = node_tags[lhs_node]; + const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel; + const lhs_is_open_slice = lhs_tag == .slice_open or + (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0); + if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) { + const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs); + + const start = if (lhs_is_slice_sentinel) start: { + const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel); + break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start); + } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs); + + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; + try emitDbgStmt(gz, cursor); + const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{ + .lhs = lhs, + .start = start, + .len = len, + .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), + .sentinel = .none, + }); + return rvalue(gz, ri, result, node); + } const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); - const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); const end = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end); try emitDbgStmt(gz, cursor); @@ -864,10 +889,36 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE return rvalue(gz, ri, result, node); }, .slice_sentinel => { + const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); + const lhs_node = node_datas[node].lhs; + const lhs_tag = node_tags[lhs_node]; + const lhs_is_slice_sentinel = lhs_tag == .slice_sentinel; + const lhs_is_open_slice = lhs_tag == .slice_open or + (lhs_is_slice_sentinel and tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel).end == 0); + if (lhs_is_open_slice and nodeIsTriviallyZero(tree, extra.start)) { + const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs); + + const start = if (lhs_is_slice_sentinel) start: { + const lhs_extra = tree.extraData(node_datas[lhs_node].rhs, Ast.Node.SliceSentinel); + break :start try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, lhs_extra.start); + } else try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs); + + const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); + const len = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; + const sentinel = try expr(gz, scope, .{ .rl = .none }, extra.sentinel); + try emitDbgStmt(gz, cursor); + const result = try gz.addPlNode(.slice_length, node, Zir.Inst.SliceLength{ + .lhs = lhs, + .start = start, + .len = len, + .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), + .sentinel = sentinel, + }); + return rvalue(gz, ri, result, node); + } const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[node].lhs); const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); - const extra = tree.extraData(node_datas[node].rhs, Ast.Node.SliceSentinel); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.start); const end = if (extra.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, extra.end) else .none; 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 .slice_start, .slice_end, .slice_sentinel, + .slice_length, .import, .switch_block, .switch_cond, diff --git a/src/Autodoc.zig b/src/Autodoc.zig index 42813f761416..1dfe036fd66a 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -755,6 +755,7 @@ const DocData = struct { string: []const u8, // direct value sliceIndex: usize, slice: Slice, + sliceLength: SliceLength, cmpxchgIndex: usize, cmpxchg: Cmpxchg, builtin: Builtin, @@ -791,6 +792,12 @@ const DocData = struct { end: ?usize = null, sentinel: ?usize = null, // index in `exprs` }; + const SliceLength = struct { + lhs: usize, + start: usize, + len: usize, + sentinel: ?usize = null, + }; const Cmpxchg = struct { name: []const u8, type: usize, @@ -1287,6 +1294,68 @@ fn walkInstruction( .expr = .{ .sliceIndex = slice_index }, }; }, + .slice_length => { + const pl_node = data[inst_index].pl_node; + const extra = file.zir.extraData(Zir.Inst.SliceLength, pl_node.payload_index); + + const slice_index = self.exprs.items.len; + try self.exprs.append(self.arena, .{ .slice = .{ .lhs = 0, .start = 0 } }); + + var lhs: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + parent_src, + extra.data.lhs, + false, + ); + var start: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + parent_src, + extra.data.start, + false, + ); + var len: DocData.WalkResult = try self.walkRef( + file, + parent_scope, + parent_src, + extra.data.len, + false, + ); + var sentinel_opt: ?DocData.WalkResult = if (extra.data.sentinel != .none) + try self.walkRef( + file, + parent_scope, + parent_src, + extra.data.sentinel, + false, + ) + else + null; + + const lhs_index = self.exprs.items.len; + try self.exprs.append(self.arena, lhs.expr); + const start_index = self.exprs.items.len; + try self.exprs.append(self.arena, start.expr); + const len_index = self.exprs.items.len; + try self.exprs.append(self.arena, len.expr); + const sentinel_index = if (sentinel_opt) |sentinel| sentinel_index: { + const index = self.exprs.items.len; + try self.exprs.append(self.arena, sentinel.expr); + break :sentinel_index index; + } else null; + self.exprs.items[slice_index] = .{ .sliceLength = .{ + .lhs = lhs_index, + .start = start_index, + .len = len_index, + .sentinel = sentinel_index, + } }; + + return DocData.WalkResult{ + .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef, + .expr = .{ .sliceIndex = slice_index }, + }; + }, // @check array_cat and array_mul .add, diff --git a/src/Sema.zig b/src/Sema.zig index b2387dfe12ba..68fbfe08d101 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -985,6 +985,7 @@ fn analyzeBodyInner( .slice_end => try sema.zirSliceEnd(block, inst), .slice_sentinel => try sema.zirSliceSentinel(block, inst), .slice_start => try sema.zirSliceStart(block, inst), + .slice_length => try sema.zirSliceLength(block, inst), .str => try sema.zirStr(block, inst), .switch_block => try sema.zirSwitchBlock(block, inst), .switch_cond => try sema.zirSwitchCond(block, inst, false), @@ -9922,7 +9923,7 @@ fn zirSliceStart(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError! const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; - return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src); + return sema.analyzeSlice(block, src, array_ptr, start, .none, .none, .unneeded, ptr_src, start_src, end_src, false); } fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -9939,7 +9940,7 @@ fn zirSliceEnd(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Ai const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; - return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src); + return sema.analyzeSlice(block, src, array_ptr, start, end, .none, .unneeded, ptr_src, start_src, end_src, false); } fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { @@ -9958,7 +9959,29 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr const start_src: LazySrcLoc = .{ .node_offset_slice_start = inst_data.src_node }; const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; - return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src); + return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src, false); +} + +fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); + + const inst_data = sema.code.instructions.items(.data)[inst].pl_node; + const src = inst_data.src(); + const extra = sema.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data; + const array_ptr = try sema.resolveInst(extra.lhs); + const start = try sema.resolveInst(extra.start); + const len = try sema.resolveInst(extra.len); + const sentinel = try sema.resolveInst(extra.sentinel); + const ptr_src: LazySrcLoc = .{ .node_offset_slice_ptr = inst_data.src_node }; + const start_src: LazySrcLoc = .{ .node_offset_slice_start = extra.start_src_node_offset }; + const end_src: LazySrcLoc = .{ .node_offset_slice_end = inst_data.src_node }; + const sentinel_src: LazySrcLoc = if (sentinel == .none) + .unneeded + else + .{ .node_offset_slice_sentinel = inst_data.src_node }; + + return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true); } fn zirSwitchCapture( @@ -29190,6 +29213,7 @@ fn analyzeSlice( ptr_src: LazySrcLoc, start_src: LazySrcLoc, end_src: LazySrcLoc, + by_length: bool, ) CompileError!Air.Inst.Ref { // Slice expressions can operate on a variable whose type is an array. This requires // the slice operand to be a pointer. In the case of a non-array, it will be a double pointer. @@ -29268,7 +29292,11 @@ fn analyzeSlice( const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()); if (!end_is_len) { - const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + const end = if (by_length) end: { + const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); + break :end try sema.coerce(block, Type.usize, uncasted_end, end_src); + } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); if (try sema.resolveMaybeUndefVal(end)) |end_val| { const len_s_val = try Value.Tag.int_u64.create( sema.arena, @@ -29305,7 +29333,11 @@ fn analyzeSlice( break :e try sema.addConstant(Type.usize, len_val); } else if (slice_ty.isSlice()) { if (!end_is_len) { - const end = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + const end = if (by_length) end: { + const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); + break :end try sema.coerce(block, Type.usize, uncasted_end, end_src); + } else try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { if (try sema.resolveMaybeUndefVal(ptr_or_slice)) |slice_val| { if (slice_val.isUndef()) { @@ -29352,7 +29384,11 @@ fn analyzeSlice( break :e try sema.analyzeSliceLen(block, src, ptr_or_slice); } if (!end_is_len) { - break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + if (by_length) { + const len = try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); + const uncasted_end = try sema.analyzeArithmetic(block, .add, start, len, src, start_src, end_src, false); + break :e try sema.coerce(block, Type.usize, uncasted_end, end_src); + } else break :e try sema.coerce(block, Type.usize, uncasted_end_opt, end_src); } return sema.fail(block, src, "slice of pointer must include end value", .{}); }; @@ -29376,7 +29412,7 @@ fn analyzeSlice( // requirement: start <= end if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| { - if (!(try sema.compareAll(start_val, .lte, end_val, Type.usize))) { + if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) { return sema.fail( block, start_src, @@ -29429,11 +29465,14 @@ fn analyzeSlice( } } - if (block.wantSafety() and !block.is_comptime) { + if (!by_length and block.wantSafety() and !block.is_comptime) { // requirement: start <= end try sema.panicStartLargerThanEnd(block, start, end); } - const new_len = try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); + const new_len = if (by_length) + try sema.coerce(block, Type.usize, uncasted_end_opt, end_src) + else + try sema.analyzeArithmetic(block, .sub, end, start, src, end_src, start_src, false); const opt_new_len_val = try sema.resolveDefinedValue(block, src, new_len); const new_ptr_ty_info = sema.typeOf(new_ptr).ptrInfo().data; diff --git a/src/Zir.zig b/src/Zir.zig index 7f9681e53336..ccff32c4355a 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -570,6 +570,10 @@ pub const Inst = struct { /// Returns a pointer to the subslice. /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceSentinel`. slice_sentinel, + /// Slice operation `array_ptr[start..][0..len]`. Optional sentinel. + /// Returns a pointer to the subslice. + /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`. + slice_length, /// Write a value to a pointer. For loading, see `load`. /// Source location is assumed to be same as previous instruction. /// Uses the `bin` union field. @@ -1135,6 +1139,7 @@ pub const Inst = struct { .slice_start, .slice_end, .slice_sentinel, + .slice_length, .import, .typeof_log2_int_type, .resolve_inferred_alloc, @@ -1430,6 +1435,7 @@ pub const Inst = struct { .slice_start, .slice_end, .slice_sentinel, + .slice_length, .import, .typeof_log2_int_type, .switch_capture, @@ -1667,6 +1673,7 @@ pub const Inst = struct { .slice_start = .pl_node, .slice_end = .pl_node, .slice_sentinel = .pl_node, + .slice_length = .pl_node, .store = .bin, .store_node = .pl_node, .store_to_block_ptr = .bin, @@ -2980,6 +2987,14 @@ pub const Inst = struct { sentinel: Ref, }; + pub const SliceLength = struct { + lhs: Ref, + start: Ref, + len: Ref, + sentinel: Ref, + start_src_node_offset: i32, + }; + /// The meaning of these operands depends on the corresponding `Tag`. pub const Bin = struct { lhs: Ref, diff --git a/src/link/Plan9/aout.zig b/src/link/Plan9/aout.zig index b39cae5166c0..d6fbb47d35c8 100644 --- a/src/link/Plan9/aout.zig +++ b/src/link/Plan9/aout.zig @@ -21,7 +21,7 @@ pub const ExecHdr = extern struct { var buf: [40]u8 = undefined; var i: u8 = 0; inline for (std.meta.fields(@This())) |f| { - std.mem.writeIntSliceBig(u32, buf[i .. i + 4], @field(self, f.name)); + std.mem.writeIntSliceBig(u32, buf[i..][0..4], @field(self, f.name)); i += 4; } return buf; diff --git a/src/print_zir.zig b/src/print_zir.zig index f228adc7ea7f..f5e84fcf5bc6 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -267,6 +267,7 @@ const Writer = struct { .slice_start => try self.writeSliceStart(stream, inst), .slice_end => try self.writeSliceEnd(stream, inst), .slice_sentinel => try self.writeSliceSentinel(stream, inst), + .slice_length => try self.writeSliceLength(stream, inst), .union_init => try self.writeUnionInit(stream, inst), @@ -756,6 +757,22 @@ const Writer = struct { try self.writeSrc(stream, inst_data.src()); } + fn writeSliceLength(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { + const inst_data = self.code.instructions.items(.data)[inst].pl_node; + const extra = self.code.extraData(Zir.Inst.SliceLength, inst_data.payload_index).data; + try self.writeInstRef(stream, extra.lhs); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.start); + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.len); + if (extra.sentinel != .none) { + try stream.writeAll(", "); + try self.writeInstRef(stream, extra.sentinel); + } + try stream.writeAll(") "); + try self.writeSrc(stream, inst_data.src()); + } + fn writeUnionInit(self: *Writer, stream: anytype, inst: Zir.Inst.Index) !void { const inst_data = self.code.instructions.items(.data)[inst].pl_node; const extra = self.code.extraData(Zir.Inst.UnionInit, inst_data.payload_index).data; diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 237ddd195bcd..29ceb061c175 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -180,6 +180,18 @@ test "slicing zero length array" { try expect(mem.eql(u32, s2, &[_]u32{})); } +test "slicing pointer by length" { + if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; + const array = [_]u8{ 1, 2, 3, 4, 5, 6, 7, 8 }; + const ptr: [*]const u8 = @ptrCast([*]const u8, &array); + const slice = ptr[1..][0..5]; + try expect(slice.len == 5); + var i: usize = 0; + while (i < slice.len) : (i += 1) { + try expect(slice[i] == i + 2); + } +} + const x = @intToPtr([*]i32, 0x1000)[0..0x500]; const y = x[0x100..]; test "compile time slice of pointer to hard coded address" { @@ -355,6 +367,10 @@ test "slice syntax resulting in pointer-to-array" { try testSlice(); try testSliceOpt(); try testSliceAlign(); + try testSliceLength(); + try testSliceLengthZ(); + try testArrayLength(); + try testArrayLengthZ(); } fn testArray() !void { @@ -465,6 +481,67 @@ test "slice syntax resulting in pointer-to-array" { try expectEqualSlices("a"[0..] ++ "b"[0..], "ab"); try expectEqualSlices("a"[0.. :0] ++ "b"[0.. :0], "ab"); } + + fn testSliceLength() !void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var slice: []u8 = &array; + comptime try expect(@TypeOf(slice[1..][0..2]) == *[2]u8); + comptime try expect(@TypeOf(slice[1..][0..4]) == *[4]u8); + comptime try expect(@TypeOf(slice[1..][0..2 :4]) == *[2:4]u8); + } + + fn testSliceLengthZ() !void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + var slice: [:0]u8 = &array; + comptime try expect(@TypeOf(slice[1..][0..2]) == *[2]u8); + comptime try expect(@TypeOf(slice[1..][0..2 :4]) == *[2:4]u8); + comptime try expect(@TypeOf(slice[1.. :0][0..2]) == *[2]u8); + comptime try expect(@TypeOf(slice[1.. :0][0..2 :4]) == *[2:4]u8); + } + + fn testArrayLength() !void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + comptime try expect(@TypeOf(array[1..][0..2]) == *[2]u8); + comptime try expect(@TypeOf(array[1..][0..4]) == *[4]u8); + comptime try expect(@TypeOf(array[1..][0..2 :4]) == *[2:4]u8); + } + + fn testArrayLengthZ() !void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + comptime try expect(@TypeOf(array[1..][0..2]) == *[2]u8); + comptime try expect(@TypeOf(array[1..][0..4]) == *[4:0]u8); + comptime try expect(@TypeOf(array[1..][0..2 :4]) == *[2:4]u8); + comptime try expect(@TypeOf(array[1.. :0][0..2]) == *[2]u8); + comptime try expect(@TypeOf(array[1.. :0][0..4]) == *[4:0]u8); + comptime try expect(@TypeOf(array[1.. :0][0..2 :4]) == *[2:4]u8); + } + + fn testMultiPointer() !void { + var array = [5]u8{ 1, 2, 3, 4, 5 }; + var ptr: [*]u8 = &array; + comptime try expect(@TypeOf(ptr[1..][0..2]) == *[2]u8); + comptime try expect(@TypeOf(ptr[1..][0..4]) == *[4]u8); + comptime try expect(@TypeOf(ptr[1..][0..2 :4]) == *[2:4]u8); + } + + fn testMultiPointerLengthZ() !void { + var array = [5:0]u8{ 1, 2, 3, 4, 5 }; + var ptr: [*]u8 = &array; + comptime try expect(@TypeOf(ptr[1..][0..2]) == *[2]u8); + comptime try expect(@TypeOf(ptr[1..][0..4]) == *[4:0]u8); + comptime try expect(@TypeOf(ptr[1..][0..2 :4]) == *[2:4]u8); + comptime try expect(@TypeOf(ptr[1.. :0][0..2]) == *[2]u8); + comptime try expect(@TypeOf(ptr[1.. :0][0..4]) == *[4:0]u8); + comptime try expect(@TypeOf(ptr[1.. :0][0..2 :4]) == *[2:4]u8); + + var ptr_z: [*:0]u8 = &array; + comptime try expect(@TypeOf(ptr_z[1..][0..2]) == *[2]u8); + comptime try expect(@TypeOf(ptr_z[1..][0..4]) == *[4:0]u8); + comptime try expect(@TypeOf(ptr_z[1..][0..2 :4]) == *[2:4]u8); + comptime try expect(@TypeOf(ptr_z[1.. :0][0..2]) == *[2]u8); + comptime try expect(@TypeOf(ptr_z[1.. :0][0..4]) == *[4:0]u8); + comptime try expect(@TypeOf(ptr_z[1.. :0][0..2 :4]) == *[2:4]u8); + } }; try S.doTheTest();