authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-02-05 19:36:14+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-02-05 19:36:14+00:00
log0f38558435a0f73c4c025b5641bd8e531f063e0c
tree439d2571f3c7bf3bdb4b532cccfe3c5eff38ce62
parent456f3c026b3d6fb289f42e442b202e0be152c9d3
signaturelock-open Commit is signed but in an unrecognized format.

compiler: provide result type to sentinel expression in slice operation

Resolves: #21867

5 files changed, 68 insertions(+), 1 deletions(-)

lib/std/zig/AstGen.zig+5-1
......@@ -920,7 +920,10 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
920920 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
921921 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.start);
922922 const end = if (full.ast.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.end) else .none;
923 const sentinel = if (full.ast.sentinel != 0) try expr(gz, scope, .{ .rl = .none }, full.ast.sentinel) else .none;
923 const sentinel = if (full.ast.sentinel != 0) s: {
924 const sentinel_ty = try gz.addUnNode(.slice_sentinel_ty, lhs, node);
925 break :s try expr(gz, scope, .{ .rl = .{ .coerced_ty = sentinel_ty } }, full.ast.sentinel);
926 } else .none;
924927 try emitDbgStmt(gz, cursor);
925928 if (sentinel != .none) {
926929 const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{
......@@ -2855,6 +2858,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
28552858 .slice_end,
28562859 .slice_sentinel,
28572860 .slice_length,
2861 .slice_sentinel_ty,
28582862 .import,
28592863 .switch_block,
28602864 .switch_block_ref,
lib/std/zig/Zir.zig+8
......@@ -599,6 +599,10 @@ pub const Inst = struct {
599599 /// Returns a pointer to the subslice.
600600 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
601601 slice_length,
602 /// Given a value which is a pointer to the LHS of a slice operation, return the sentinel
603 /// type, used as the result type of the slice sentinel (i.e. `s` in `lhs[a..b :s]`).
604 /// Uses the `un_node` field. AST node is the slice syntax. Operand is `lhs`.
605 slice_sentinel_ty,
602606 /// Same as `store` except provides a source location.
603607 /// Uses the `pl_node` union field. Payload is `Bin`.
604608 store_node,
......@@ -1185,6 +1189,7 @@ pub const Inst = struct {
11851189 .slice_end,
11861190 .slice_sentinel,
11871191 .slice_length,
1192 .slice_sentinel_ty,
11881193 .import,
11891194 .typeof_log2_int_type,
11901195 .resolve_inferred_alloc,
......@@ -1472,6 +1477,7 @@ pub const Inst = struct {
14721477 .slice_end,
14731478 .slice_sentinel,
14741479 .slice_length,
1480 .slice_sentinel_ty,
14751481 .import,
14761482 .typeof_log2_int_type,
14771483 .switch_block,
......@@ -1702,6 +1708,7 @@ pub const Inst = struct {
17021708 .slice_end = .pl_node,
17031709 .slice_sentinel = .pl_node,
17041710 .slice_length = .pl_node,
1711 .slice_sentinel_ty = .un_node,
17051712 .store_node = .pl_node,
17061713 .store_to_inferred_ptr = .pl_node,
17071714 .str = .str,
......@@ -4162,6 +4169,7 @@ fn findTrackableInner(
41624169 .slice_end,
41634170 .slice_sentinel,
41644171 .slice_length,
4172 .slice_sentinel_ty,
41654173 .store_node,
41664174 .store_to_inferred_ptr,
41674175 .str,
src/Sema.zig+41
......@@ -1197,6 +1197,7 @@ fn analyzeBodyInner(
11971197 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
11981198 .slice_start => try sema.zirSliceStart(block, inst),
11991199 .slice_length => try sema.zirSliceLength(block, inst),
1200 .slice_sentinel_ty => try sema.zirSliceSentinelTy(block, inst),
12001201 .str => try sema.zirStr(inst),
12011202 .switch_block => try sema.zirSwitchBlock(block, inst, false),
12021203 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true),
......@@ -10753,6 +10754,46 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
1075310754 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
1075410755}
1075510756
10757fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
10758 const tracy = trace(@src());
10759 defer tracy.end();
10760
10761 const pt = sema.pt;
10762 const zcu = pt.zcu;
10763
10764 const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node;
10765
10766 const src = block.nodeOffset(inst_data.src_node);
10767 const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node });
10768 const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node });
10769
10770 // This is like the logic in `analyzeSlice`; since we've evaluated the LHS as an lvalue, we will
10771 // have a double pointer if it was already a pointer.
10772
10773 const lhs_ptr_ty = sema.typeOf(try sema.resolveInst(inst_data.operand));
10774 const lhs_ty = switch (lhs_ptr_ty.zigTypeTag(zcu)) {
10775 .pointer => lhs_ptr_ty.childType(zcu),
10776 else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{lhs_ptr_ty.fmt(pt)}),
10777 };
10778
10779 const sentinel_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) {
10780 .array => lhs_ty.childType(zcu),
10781 .pointer => switch (lhs_ty.ptrSize(zcu)) {
10782 .many, .c, .slice => lhs_ty.childType(zcu),
10783 .one => s: {
10784 const lhs_elem_ty = lhs_ty.childType(zcu);
10785 break :s switch (lhs_elem_ty.zigTypeTag(zcu)) {
10786 .array => lhs_elem_ty.childType(zcu), // array element type
10787 else => return sema.fail(block, sentinel_src, "slice of single-item pointer cannot have sentinel", .{}),
10788 };
10789 },
10790 },
10791 else => return sema.fail(block, src, "slice of non-array type '{}'", .{lhs_ty.fmt(pt)}),
10792 };
10793
10794 return Air.internedToRef(sentinel_ty.toIntern());
10795}
10796
1075610797/// Holds common data used when analyzing or resolving switch prong bodies,
1075710798/// including setting up captures.
1075810799const SwitchProngAnalysis = struct {
src/print_zir.zig+1
......@@ -208,6 +208,7 @@ const Writer = struct {
208208 .anyframe_type,
209209 .bit_not,
210210 .bool_not,
211 .slice_sentinel_ty,
211212 .negate,
212213 .negate_wrap,
213214 .load,
test/behavior/slice.zig+13
......@@ -1037,3 +1037,16 @@ test "peer slices keep abi alignment with empty struct" {
10371037 comptime assert(@TypeOf(slice) == []const u32);
10381038 try expect(slice.len == 0);
10391039}
1040
1041test "sentinel expression in slice operation has result type" {
1042 const sentinel = std.math.maxInt(u16);
1043
1044 const arr: [3]u16 = .{ 1, 2, sentinel };
1045 const slice = arr[0..2 :@intCast(sentinel)];
1046
1047 comptime assert(@TypeOf(slice) == *const [2:sentinel]u16);
1048 comptime assert(slice[2] == sentinel);
1049 comptime assert(slice.len == 2);
1050 comptime assert(slice[0] == 1);
1051 comptime assert(slice[1] == 2);
1052}