Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions doc/langref.html.in
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
19 changes: 18 additions & 1 deletion lib/docs/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -4850,4 +4867,4 @@ function RadixTree() {

// BUT!

// We want to be able to search "Hash", for example!
// We want to be able to search "Hash", for example!
2 changes: 1 addition & 1 deletion lib/std/Build/Cache/DepTokenizer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
4 changes: 2 additions & 2 deletions lib/std/array_list.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion lib/std/bounded_array.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion lib/std/compress/deflate/decompressor.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions lib/std/compress/deflate/huffman_bit_writer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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);
Expand Down
2 changes: 1 addition & 1 deletion lib/std/hash/crc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
Expand Down
2 changes: 1 addition & 1 deletion lib/std/hash/wyhash.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
6 changes: 3 additions & 3 deletions lib/std/json.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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;
},
Expand Down
2 changes: 1 addition & 1 deletion lib/std/math/big/int.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/std/net.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/std/os/windows.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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});
Expand Down
2 changes: 1 addition & 1 deletion lib/std/testing.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
Expand Down
2 changes: 1 addition & 1 deletion lib/std/unicode.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
56 changes: 54 additions & 2 deletions src/AstGen.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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,
Expand Down
69 changes: 69 additions & 0 deletions src/Autodoc.zig
Original file line number Diff line number Diff line change
Expand Up @@ -755,6 +755,7 @@ const DocData = struct {
string: []const u8, // direct value
sliceIndex: usize,
slice: Slice,
sliceLength: SliceLength,
cmpxchgIndex: usize,
cmpxchg: Cmpxchg,
builtin: Builtin,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Loading