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...@@ -920,7 +920,10 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE
920 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);920 const cursor = maybeAdvanceSourceCursorToMainToken(gz, node);
921 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.start);921 const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.start);
922 const end = if (full.ast.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.end) else .none;922 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;
924 try emitDbgStmt(gz, cursor);927 try emitDbgStmt(gz, cursor);
925 if (sentinel != .none) {928 if (sentinel != .none) {
926 const result = try gz.addPlNode(.slice_sentinel, node, Zir.Inst.SliceSentinel{929 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...@@ -2855,6 +2858,7 @@ fn addEnsureResult(gz: *GenZir, maybe_unused_result: Zir.Inst.Ref, statement: As
2855 .slice_end,2858 .slice_end,
2856 .slice_sentinel,2859 .slice_sentinel,
2857 .slice_length,2860 .slice_length,
2861 .slice_sentinel_ty,
2858 .import,2862 .import,
2859 .switch_block,2863 .switch_block,
2860 .switch_block_ref,2864 .switch_block_ref,
lib/std/zig/Zir.zig+8
...@@ -599,6 +599,10 @@ pub const Inst = struct {...@@ -599,6 +599,10 @@ pub const Inst = struct {
599 /// Returns a pointer to the subslice.599 /// Returns a pointer to the subslice.
600 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.600 /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`.
601 slice_length,601 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,
602 /// Same as `store` except provides a source location.606 /// Same as `store` except provides a source location.
603 /// Uses the `pl_node` union field. Payload is `Bin`.607 /// Uses the `pl_node` union field. Payload is `Bin`.
604 store_node,608 store_node,
...@@ -1185,6 +1189,7 @@ pub const Inst = struct {...@@ -1185,6 +1189,7 @@ pub const Inst = struct {
1185 .slice_end,1189 .slice_end,
1186 .slice_sentinel,1190 .slice_sentinel,
1187 .slice_length,1191 .slice_length,
1192 .slice_sentinel_ty,
1188 .import,1193 .import,
1189 .typeof_log2_int_type,1194 .typeof_log2_int_type,
1190 .resolve_inferred_alloc,1195 .resolve_inferred_alloc,
...@@ -1472,6 +1477,7 @@ pub const Inst = struct {...@@ -1472,6 +1477,7 @@ pub const Inst = struct {
1472 .slice_end,1477 .slice_end,
1473 .slice_sentinel,1478 .slice_sentinel,
1474 .slice_length,1479 .slice_length,
1480 .slice_sentinel_ty,
1475 .import,1481 .import,
1476 .typeof_log2_int_type,1482 .typeof_log2_int_type,
1477 .switch_block,1483 .switch_block,
...@@ -1702,6 +1708,7 @@ pub const Inst = struct {...@@ -1702,6 +1708,7 @@ pub const Inst = struct {
1702 .slice_end = .pl_node,1708 .slice_end = .pl_node,
1703 .slice_sentinel = .pl_node,1709 .slice_sentinel = .pl_node,
1704 .slice_length = .pl_node,1710 .slice_length = .pl_node,
1711 .slice_sentinel_ty = .un_node,
1705 .store_node = .pl_node,1712 .store_node = .pl_node,
1706 .store_to_inferred_ptr = .pl_node,1713 .store_to_inferred_ptr = .pl_node,
1707 .str = .str,1714 .str = .str,
...@@ -4162,6 +4169,7 @@ fn findTrackableInner(...@@ -4162,6 +4169,7 @@ fn findTrackableInner(
4162 .slice_end,4169 .slice_end,
4163 .slice_sentinel,4170 .slice_sentinel,
4164 .slice_length,4171 .slice_length,
4172 .slice_sentinel_ty,
4165 .store_node,4173 .store_node,
4166 .store_to_inferred_ptr,4174 .store_to_inferred_ptr,
4167 .str,4175 .str,
src/Sema.zig+41
...@@ -1197,6 +1197,7 @@ fn analyzeBodyInner(...@@ -1197,6 +1197,7 @@ fn analyzeBodyInner(
1197 .slice_sentinel => try sema.zirSliceSentinel(block, inst),1197 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
1198 .slice_start => try sema.zirSliceStart(block, inst),1198 .slice_start => try sema.zirSliceStart(block, inst),
1199 .slice_length => try sema.zirSliceLength(block, inst),1199 .slice_length => try sema.zirSliceLength(block, inst),
1200 .slice_sentinel_ty => try sema.zirSliceSentinelTy(block, inst),
1200 .str => try sema.zirStr(inst),1201 .str => try sema.zirStr(inst),
1201 .switch_block => try sema.zirSwitchBlock(block, inst, false),1202 .switch_block => try sema.zirSwitchBlock(block, inst, false),
1202 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true),1203 .switch_block_ref => try sema.zirSwitchBlock(block, inst, true),
...@@ -10753,6 +10754,46 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError...@@ -10753,6 +10754,46 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError
10753 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);10754 return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true);
10754}10755}
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
10756/// Holds common data used when analyzing or resolving switch prong bodies,10797/// Holds common data used when analyzing or resolving switch prong bodies,
10757/// including setting up captures.10798/// including setting up captures.
10758const SwitchProngAnalysis = struct {10799const SwitchProngAnalysis = struct {
src/print_zir.zig+1
...@@ -208,6 +208,7 @@ const Writer = struct {...@@ -208,6 +208,7 @@ const Writer = struct {
208 .anyframe_type,208 .anyframe_type,
209 .bit_not,209 .bit_not,
210 .bool_not,210 .bool_not,
211 .slice_sentinel_ty,
211 .negate,212 .negate,
212 .negate_wrap,213 .negate_wrap,
213 .load,214 .load,
test/behavior/slice.zig+13
...@@ -1037,3 +1037,16 @@ test "peer slices keep abi alignment with empty struct" {...@@ -1037,3 +1037,16 @@ test "peer slices keep abi alignment with empty struct" {
1037 comptime assert(@TypeOf(slice) == []const u32);1037 comptime assert(@TypeOf(slice) == []const u32);
1038 try expect(slice.len == 0);1038 try expect(slice.len == 0);
1039}1039}
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}