From a62b5d84d834757682be9b5a0606ff8168c1d594 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 30 Apr 2023 12:25:13 +1000 Subject: [PATCH 01/13] zir: add slice_length tag --- src/AstGen.zig | 1 + src/Autodoc.zig | 1 + src/Sema.zig | 10 ++++++++++ src/Zir.zig | 14 ++++++++++++++ src/print_zir.zig | 13 +++++++++++++ 5 files changed, 39 insertions(+) diff --git a/src/AstGen.zig b/src/AstGen.zig index 749e3d28c461..0233a9819d2f 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -2557,6 +2557,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..db24f747f79b 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -1287,6 +1287,7 @@ fn walkInstruction( .expr = .{ .sliceIndex = slice_index }, }; }, + .slice_length => @panic("TODO: implement walkInstruction for .slice_length"), // @check array_cat and array_mul .add, diff --git a/src/Sema.zig b/src/Sema.zig index b2387dfe12ba..d225a8f7b39f 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), @@ -9961,6 +9962,15 @@ fn zirSliceSentinel(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileErr return sema.analyzeSlice(block, src, array_ptr, start, end, sentinel, sentinel_src, ptr_src, start_src, end_src); } +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(); + return sema.fail(block, src, "TODO: implement .slice_length", .{}); +} + fn zirSwitchCapture( sema: *Sema, block: *Block, diff --git a/src/Zir.zig b/src/Zir.zig index 7f9681e53336..27ea02e64275 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]`. No 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,13 @@ pub const Inst = struct { sentinel: Ref, }; + pub const SliceLength = struct { + lhs: Ref, + start: Ref, + len: 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/print_zir.zig b/src/print_zir.zig index f228adc7ea7f..6d1a716d3c3c 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,18 @@ 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); + 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; From e58a0c5e9e9a6eab7f5efdc60b0f3d5b9c106786 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 30 Apr 2023 15:00:28 +1000 Subject: [PATCH 02/13] sema: implement slice_length ZIR instruction --- src/Sema.zig | 133 +++++++++++++++++++++++++++++---------------------- 1 file changed, 76 insertions(+), 57 deletions(-) diff --git a/src/Sema.zig b/src/Sema.zig index d225a8f7b39f..bcb13ec6f0c6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9923,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 { @@ -9940,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 { @@ -9959,7 +9959,7 @@ 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 { @@ -9968,7 +9968,15 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const src = inst_data.src(); - return sema.fail(block, src, "TODO: implement .slice_length", .{}); + 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 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 }; + + return sema.analyzeSlice(block, src, array_ptr, start, len, .none, .unneeded, ptr_src, start_src, end_src, true); } fn zirSwitchCapture( @@ -29200,6 +29208,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. @@ -29274,7 +29283,12 @@ fn analyzeSlice( // we might learn of the length because it is a comptime-known slice value. var end_is_len = uncasted_end_opt == .none; const end = e: { - if (array_ty.zigTypeTag() == .Array) { + if (by_length and !end_is_len) { + 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); + const end = try sema.coerce(block, Type.usize, uncasted_end, end_src); + break :e end; + } else if (array_ty.zigTypeTag() == .Array) { const len_val = try Value.Tag.int_u64.create(sema.arena, array_ty.arrayLen()); if (!end_is_len) { @@ -29384,66 +29398,71 @@ fn analyzeSlice( const slice_sentinel = if (sentinel_opt != .none) sentinel else null; // 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))) { - return sema.fail( - block, - start_src, - "start index {} is larger than end index {}", - .{ - start_val.fmtValue(Type.usize, mod), - end_val.fmtValue(Type.usize, mod), - }, - ); - } - if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: { - const expected_sentinel = sentinel orelse break :sentinel_check; - const start_int = start_val.getUnsignedInt(sema.mod.getTarget()).?; - const end_int = end_val.getUnsignedInt(sema.mod.getTarget()).?; - const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); - - const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod); - const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false); - const actual_sentinel = switch (res) { - .runtime_load => break :sentinel_check, - .val => |v| v, - .needed_well_defined => |ty| return sema.fail( - block, - src, - "comptime dereference requires '{}' to have a well-defined layout, but it does not.", - .{ty.fmt(sema.mod)}, - ), - .out_of_bounds => |ty| return sema.fail( + if (!by_length) { + 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))) { + return sema.fail( block, - end_src, - "slice end index {d} exceeds bounds of containing decl of type '{}'", - .{ end_int, ty.fmt(sema.mod) }, - ), - }; + start_src, + "start index {} is larger than end index {}", + .{ + start_val.fmtValue(Type.usize, mod), + end_val.fmtValue(Type.usize, mod), + }, + ); + } + if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: { + const expected_sentinel = sentinel orelse break :sentinel_check; + const start_int = start_val.getUnsignedInt(sema.mod.getTarget()).?; + const end_int = end_val.getUnsignedInt(sema.mod.getTarget()).?; + const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); + + const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod); + const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false); + const actual_sentinel = switch (res) { + .runtime_load => break :sentinel_check, + .val => |v| v, + .needed_well_defined => |ty| return sema.fail( + block, + src, + "comptime dereference requires '{}' to have a well-defined layout, but it does not.", + .{ty.fmt(sema.mod)}, + ), + .out_of_bounds => |ty| return sema.fail( + block, + end_src, + "slice end index {d} exceeds bounds of containing decl of type '{}'", + .{ end_int, ty.fmt(sema.mod) }, + ), + }; - if (!actual_sentinel.eql(expected_sentinel, elem_ty, sema.mod)) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{ - expected_sentinel.fmtValue(elem_ty, sema.mod), - actual_sentinel.fmtValue(elem_ty, sema.mod), - }); + if (!actual_sentinel.eql(expected_sentinel, elem_ty, sema.mod)) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{ + expected_sentinel.fmtValue(elem_ty, sema.mod), + actual_sentinel.fmtValue(elem_ty, sema.mod), + }); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(msg); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } } } } - } - if (block.wantSafety() and !block.is_comptime) { - // requirement: start <= end - try sema.panicStartLargerThanEnd(block, start, end); + if (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; From 9fb5b047e97896d7b25e897dc3737769d6b35a78 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 30 Apr 2023 15:01:21 +1000 Subject: [PATCH 03/13] astgen: lower s[start..][0..len] to slice_length ZIR --- src/AstGen.zig | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index 0233a9819d2f..c8d079f1e1a1 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -849,10 +849,25 @@ 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; + if (node_tags[lhs_node] == .slice_open and nodeIsTriviallyZero(tree, extra.start)) { + const lhs = try expr(gz, scope, .{ .rl = .ref }, node_datas[lhs_node].lhs); + const start = 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), + }); + 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); From 8aa70cf6cd88b6103200d558c60295550c7b911d Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 30 Apr 2023 14:38:11 +1000 Subject: [PATCH 04/13] sema: omit extraneous addition when safety is unwanted --- src/Sema.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Sema.zig b/src/Sema.zig index bcb13ec6f0c6..17ba89a6e309 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -29284,6 +29284,7 @@ fn analyzeSlice( var end_is_len = uncasted_end_opt == .none; const end = e: { if (by_length and !end_is_len) { + if (!block.wantSafety()) break :e undefined; 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); const end = try sema.coerce(block, Type.usize, uncasted_end, end_src); From 2c2a0402c65986674d427390292f2fe38474c90f Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 30 Apr 2023 16:54:25 +1000 Subject: [PATCH 05/13] autodoc: implement slice_length case in walkInstruction --- src/Autodoc.zig | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/src/Autodoc.zig b/src/Autodoc.zig index db24f747f79b..b068c260efec 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -1287,7 +1287,48 @@ fn walkInstruction( .expr = .{ .sliceIndex = slice_index }, }; }, - .slice_length => @panic("TODO: implement walkInstruction for .slice_length"), + .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, + ); + + 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); + self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = len_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, From 7c8d60e814b985d3aab43c7c467f424596942ef9 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Mon, 1 May 2023 15:37:25 +1000 Subject: [PATCH 06/13] test: add behavior tests for slice-by-length --- test/behavior/slice.zig | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 237ddd195bcd..51538d9b10a7 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -355,6 +355,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 +469,40 @@ 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); + } }; try S.doTheTest(); From 64e319f5555a25dbf4c4d7e65289f7f96d6588b4 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Sun, 30 Apr 2023 23:50:08 +1000 Subject: [PATCH 07/13] add optional sentinel to slice_length ZIR --- src/AstGen.zig | 42 +++++++++++++-- src/Autodoc.zig | 17 +++++- src/Sema.zig | 131 ++++++++++++++++++++++++---------------------- src/Zir.zig | 3 +- src/print_zir.zig | 4 ++ 5 files changed, 129 insertions(+), 68 deletions(-) diff --git a/src/AstGen.zig b/src/AstGen.zig index c8d079f1e1a1..8df8f0e1cf16 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -851,9 +851,18 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .slice => { const extra = tree.extraData(node_datas[node].rhs, Ast.Node.Slice); const lhs_node = node_datas[node].lhs; - if (node_tags[lhs_node] == .slice_open and nodeIsTriviallyZero(tree, extra.start)) { + 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 = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, node_datas[lhs_node].rhs); + + 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); @@ -862,6 +871,7 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE .start = start, .len = len, .start_src_node_offset = gz.nodeIndexToRelative(lhs_node), + .sentinel = .none, }); return rvalue(gz, ri, result, node); } @@ -879,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); diff --git a/src/Autodoc.zig b/src/Autodoc.zig index b068c260efec..eba53b7e17c6 100644 --- a/src/Autodoc.zig +++ b/src/Autodoc.zig @@ -1315,6 +1315,16 @@ fn walkInstruction( 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); @@ -1322,7 +1332,12 @@ fn walkInstruction( try self.exprs.append(self.arena, start.expr); const len_index = self.exprs.items.len; try self.exprs.append(self.arena, len.expr); - self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = len_index } }; + 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] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = len_index, .sentinel = sentinel_index } }; return DocData.WalkResult{ .typeRef = self.decls.items[lhs.expr.declRef.Analyzed].value.typeRef, diff --git a/src/Sema.zig b/src/Sema.zig index 17ba89a6e309..8deca97505df 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -9972,11 +9972,16 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError 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, .none, .unneeded, ptr_src, start_src, end_src, true); + return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true); } fn zirSwitchCapture( @@ -29283,17 +29288,15 @@ fn analyzeSlice( // we might learn of the length because it is a comptime-known slice value. var end_is_len = uncasted_end_opt == .none; const end = e: { - if (by_length and !end_is_len) { - if (!block.wantSafety()) break :e undefined; - 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); - const end = try sema.coerce(block, Type.usize, uncasted_end, end_src); - break :e end; - } else if (array_ty.zigTypeTag() == .Array) { + if (array_ty.zigTypeTag() == .Array) { 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, @@ -29330,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()) { @@ -29399,66 +29406,64 @@ fn analyzeSlice( const slice_sentinel = if (sentinel_opt != .none) sentinel else null; // requirement: start <= end - if (!by_length) { - 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))) { - return sema.fail( + if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| { + if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| { + if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) { + return sema.fail( + block, + start_src, + "start index {} is larger than end index {}", + .{ + start_val.fmtValue(Type.usize, mod), + end_val.fmtValue(Type.usize, mod), + }, + ); + } + if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: { + const expected_sentinel = sentinel orelse break :sentinel_check; + const start_int = start_val.getUnsignedInt(sema.mod.getTarget()).?; + const end_int = end_val.getUnsignedInt(sema.mod.getTarget()).?; + const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); + + const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod); + const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false); + const actual_sentinel = switch (res) { + .runtime_load => break :sentinel_check, + .val => |v| v, + .needed_well_defined => |ty| return sema.fail( block, - start_src, - "start index {} is larger than end index {}", - .{ - start_val.fmtValue(Type.usize, mod), - end_val.fmtValue(Type.usize, mod), - }, - ); - } - if (try sema.resolveMaybeUndefVal(new_ptr)) |ptr_val| sentinel_check: { - const expected_sentinel = sentinel orelse break :sentinel_check; - const start_int = start_val.getUnsignedInt(sema.mod.getTarget()).?; - const end_int = end_val.getUnsignedInt(sema.mod.getTarget()).?; - const sentinel_index = try sema.usizeCast(block, end_src, end_int - start_int); - - const elem_ptr = try ptr_val.elemPtr(sema.typeOf(new_ptr), sema.arena, sentinel_index, sema.mod); - const res = try sema.pointerDerefExtra(block, src, elem_ptr, elem_ty, false); - const actual_sentinel = switch (res) { - .runtime_load => break :sentinel_check, - .val => |v| v, - .needed_well_defined => |ty| return sema.fail( - block, - src, - "comptime dereference requires '{}' to have a well-defined layout, but it does not.", - .{ty.fmt(sema.mod)}, - ), - .out_of_bounds => |ty| return sema.fail( - block, - end_src, - "slice end index {d} exceeds bounds of containing decl of type '{}'", - .{ end_int, ty.fmt(sema.mod) }, - ), - }; + src, + "comptime dereference requires '{}' to have a well-defined layout, but it does not.", + .{ty.fmt(sema.mod)}, + ), + .out_of_bounds => |ty| return sema.fail( + block, + end_src, + "slice end index {d} exceeds bounds of containing decl of type '{}'", + .{ end_int, ty.fmt(sema.mod) }, + ), + }; - if (!actual_sentinel.eql(expected_sentinel, elem_ty, sema.mod)) { - const msg = msg: { - const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{}); - errdefer msg.destroy(sema.gpa); - try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{ - expected_sentinel.fmtValue(elem_ty, sema.mod), - actual_sentinel.fmtValue(elem_ty, sema.mod), - }); + if (!actual_sentinel.eql(expected_sentinel, elem_ty, sema.mod)) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "value in memory does not match slice sentinel", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, src, msg, "expected '{}', found '{}'", .{ + expected_sentinel.fmtValue(elem_ty, sema.mod), + actual_sentinel.fmtValue(elem_ty, sema.mod), + }); - break :msg msg; - }; - return sema.failWithOwnedErrorMsg(msg); - } + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); } } } + } - if (block.wantSafety() and !block.is_comptime) { - // requirement: start <= end - try sema.panicStartLargerThanEnd(block, start, end); - } + if (!by_length and block.wantSafety() and !block.is_comptime) { + // requirement: start <= end + try sema.panicStartLargerThanEnd(block, start, end); } const new_len = if (by_length) try sema.coerce(block, Type.usize, uncasted_end_opt, end_src) diff --git a/src/Zir.zig b/src/Zir.zig index 27ea02e64275..ccff32c4355a 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -570,7 +570,7 @@ 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]`. No 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, @@ -2991,6 +2991,7 @@ pub const Inst = struct { lhs: Ref, start: Ref, len: Ref, + sentinel: Ref, start_src_node_offset: i32, }; diff --git a/src/print_zir.zig b/src/print_zir.zig index 6d1a716d3c3c..f5e84fcf5bc6 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -765,6 +765,10 @@ const Writer = struct { 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()); } From 5bb8e9cd975ea68c924d04be6608f96b41204b7f Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 2 May 2023 12:51:58 +1000 Subject: [PATCH 08/13] langref: mention slice-by-length pattern --- doc/langref.html.in | 8 ++++++++ 1 file changed, 8 insertions(+) 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); From 2c5924c59a148335e5025b72a5ba98d765ed771d Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 2 May 2023 22:04:15 +1000 Subject: [PATCH 09/13] autodoc: fix support for slice_length ZIR instruction --- lib/docs/main.js | 19 ++++++++++++++++++- src/Autodoc.zig | 14 +++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) 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/src/Autodoc.zig b/src/Autodoc.zig index eba53b7e17c6..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, @@ -1337,7 +1344,12 @@ fn walkInstruction( try self.exprs.append(self.arena, sentinel.expr); break :sentinel_index index; } else null; - self.exprs.items[slice_index] = .{ .slice = .{ .lhs = lhs_index, .start = start_index, .end = len_index, .sentinel = sentinel_index } }; + 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, From bd3360e03d89fe947e3728ccacd4274653926376 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Tue, 2 May 2023 22:08:54 +1000 Subject: [PATCH 10/13] convert s[start..start+len] to s[start..][0..len] --- lib/std/Build/Cache/DepTokenizer.zig | 2 +- lib/std/array_list.zig | 4 ++-- lib/std/bounded_array.zig | 2 +- lib/std/compress/deflate/decompressor.zig | 2 +- lib/std/compress/deflate/huffman_bit_writer.zig | 6 +++--- lib/std/hash/crc.zig | 2 +- lib/std/hash/wyhash.zig | 2 +- lib/std/json.zig | 6 +++--- lib/std/math/big/int.zig | 2 +- lib/std/net.zig | 2 +- lib/std/os/windows.zig | 4 ++-- lib/std/testing.zig | 2 +- lib/std/unicode.zig | 2 +- src/link/Plan9/aout.zig | 2 +- 14 files changed, 20 insertions(+), 20 deletions(-) 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/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; From cba9077cc12b953dd00369f9c0340d7667af1611 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Wed, 3 May 2023 11:30:30 +1000 Subject: [PATCH 11/13] sema: fix slice by length non-array, non-slice case --- src/Sema.zig | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/Sema.zig b/src/Sema.zig index 8deca97505df..68fbfe08d101 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -29384,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", .{}); }; From e507f0c0aa2267f003ba2afb30448fc723d54418 Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Wed, 3 May 2023 12:51:40 +1000 Subject: [PATCH 12/13] test: add behavior tests for pointer slice-by-length --- test/behavior/slice.zig | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 51538d9b10a7..575665cf7f3b 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -180,6 +180,17 @@ test "slicing zero length array" { try expect(mem.eql(u32, s2, &[_]u32{})); } +test "slicing pointer by length" { + 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" { @@ -503,6 +514,33 @@ test "slice syntax resulting in pointer-to-array" { 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(); From 4d296debefccbd80f2007685a27386b7434464dd Mon Sep 17 00:00:00 2001 From: dweiller <4678790+dweiller@users.noreplay.github.com> Date: Thu, 4 May 2023 10:58:30 +1000 Subject: [PATCH 13/13] test: disable by-length slice test on wasm backend --- test/behavior/slice.zig | 1 + 1 file changed, 1 insertion(+) diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 575665cf7f3b..29ceb061c175 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -181,6 +181,7 @@ test "slicing zero length array" { } 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];