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();