diff --git a/lib/std/zig/AstGen.zig b/lib/std/zig/AstGen.zig index c105a371ef..d43a837b2f 100644 --- a/lib/std/zig/AstGen.zig +++ b/lib/std/zig/AstGen.zig @@ -920,7 +920,10 @@ fn expr(gz: *GenZir, scope: *Scope, ri: ResultInfo, node: Ast.Node.Index) InnerE const cursor = maybeAdvanceSourceCursorToMainToken(gz, node); const start = try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.start); const end = if (full.ast.end != 0) try expr(gz, scope, .{ .rl = .{ .coerced_ty = .usize_type } }, full.ast.end) else .none; - const sentinel = if (full.ast.sentinel != 0) try expr(gz, scope, .{ .rl = .none }, full.ast.sentinel) else .none; + const sentinel = if (full.ast.sentinel != 0) s: { + const sentinel_ty = try gz.addUnNode(.slice_sentinel_ty, lhs, node); + break :s try expr(gz, scope, .{ .rl = .{ .coerced_ty = sentinel_ty } }, full.ast.sentinel); + } else .none; try emitDbgStmt(gz, cursor); if (sentinel != .none) { 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 .slice_end, .slice_sentinel, .slice_length, + .slice_sentinel_ty, .import, .switch_block, .switch_block_ref, diff --git a/lib/std/zig/Zir.zig b/lib/std/zig/Zir.zig index 092a354de6..32872eeabc 100644 --- a/lib/std/zig/Zir.zig +++ b/lib/std/zig/Zir.zig @@ -599,6 +599,10 @@ pub const Inst = struct { /// Returns a pointer to the subslice. /// Uses the `pl_node` field. AST node is the slice syntax. Payload is `SliceLength`. slice_length, + /// Given a value which is a pointer to the LHS of a slice operation, return the sentinel + /// type, used as the result type of the slice sentinel (i.e. `s` in `lhs[a..b :s]`). + /// Uses the `un_node` field. AST node is the slice syntax. Operand is `lhs`. + slice_sentinel_ty, /// Same as `store` except provides a source location. /// Uses the `pl_node` union field. Payload is `Bin`. store_node, @@ -1185,6 +1189,7 @@ pub const Inst = struct { .slice_end, .slice_sentinel, .slice_length, + .slice_sentinel_ty, .import, .typeof_log2_int_type, .resolve_inferred_alloc, @@ -1472,6 +1477,7 @@ pub const Inst = struct { .slice_end, .slice_sentinel, .slice_length, + .slice_sentinel_ty, .import, .typeof_log2_int_type, .switch_block, @@ -1702,6 +1708,7 @@ pub const Inst = struct { .slice_end = .pl_node, .slice_sentinel = .pl_node, .slice_length = .pl_node, + .slice_sentinel_ty = .un_node, .store_node = .pl_node, .store_to_inferred_ptr = .pl_node, .str = .str, @@ -4162,6 +4169,7 @@ fn findTrackableInner( .slice_end, .slice_sentinel, .slice_length, + .slice_sentinel_ty, .store_node, .store_to_inferred_ptr, .str, diff --git a/src/Sema.zig b/src/Sema.zig index af077c5150..7b8999a211 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -1197,6 +1197,7 @@ fn analyzeBodyInner( .slice_sentinel => try sema.zirSliceSentinel(block, inst), .slice_start => try sema.zirSliceStart(block, inst), .slice_length => try sema.zirSliceLength(block, inst), + .slice_sentinel_ty => try sema.zirSliceSentinelTy(block, inst), .str => try sema.zirStr(inst), .switch_block => try sema.zirSwitchBlock(block, inst, false), .switch_block_ref => try sema.zirSwitchBlock(block, inst, true), @@ -10753,6 +10754,46 @@ fn zirSliceLength(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError return sema.analyzeSlice(block, src, array_ptr, start, len, sentinel, sentinel_src, ptr_src, start_src, end_src, true); } +fn zirSliceSentinelTy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { + const tracy = trace(@src()); + defer tracy.end(); + + const pt = sema.pt; + const zcu = pt.zcu; + + const inst_data = sema.code.instructions.items(.data)[@intFromEnum(inst)].un_node; + + const src = block.nodeOffset(inst_data.src_node); + const ptr_src = block.src(.{ .node_offset_slice_ptr = inst_data.src_node }); + const sentinel_src = block.src(.{ .node_offset_slice_sentinel = inst_data.src_node }); + + // This is like the logic in `analyzeSlice`; since we've evaluated the LHS as an lvalue, we will + // have a double pointer if it was already a pointer. + + const lhs_ptr_ty = sema.typeOf(try sema.resolveInst(inst_data.operand)); + const lhs_ty = switch (lhs_ptr_ty.zigTypeTag(zcu)) { + .pointer => lhs_ptr_ty.childType(zcu), + else => return sema.fail(block, ptr_src, "expected pointer, found '{}'", .{lhs_ptr_ty.fmt(pt)}), + }; + + const sentinel_ty: Type = switch (lhs_ty.zigTypeTag(zcu)) { + .array => lhs_ty.childType(zcu), + .pointer => switch (lhs_ty.ptrSize(zcu)) { + .many, .c, .slice => lhs_ty.childType(zcu), + .one => s: { + const lhs_elem_ty = lhs_ty.childType(zcu); + break :s switch (lhs_elem_ty.zigTypeTag(zcu)) { + .array => lhs_elem_ty.childType(zcu), // array element type + else => return sema.fail(block, sentinel_src, "slice of single-item pointer cannot have sentinel", .{}), + }; + }, + }, + else => return sema.fail(block, src, "slice of non-array type '{}'", .{lhs_ty.fmt(pt)}), + }; + + return Air.internedToRef(sentinel_ty.toIntern()); +} + /// Holds common data used when analyzing or resolving switch prong bodies, /// including setting up captures. const SwitchProngAnalysis = struct { diff --git a/src/print_zir.zig b/src/print_zir.zig index 0757ca83de..24a9663376 100644 --- a/src/print_zir.zig +++ b/src/print_zir.zig @@ -208,6 +208,7 @@ const Writer = struct { .anyframe_type, .bit_not, .bool_not, + .slice_sentinel_ty, .negate, .negate_wrap, .load, diff --git a/test/behavior/slice.zig b/test/behavior/slice.zig index 52680b87f3..f7cf2d7123 100644 --- a/test/behavior/slice.zig +++ b/test/behavior/slice.zig @@ -1037,3 +1037,16 @@ test "peer slices keep abi alignment with empty struct" { comptime assert(@TypeOf(slice) == []const u32); try expect(slice.len == 0); } + +test "sentinel expression in slice operation has result type" { + const sentinel = std.math.maxInt(u16); + + const arr: [3]u16 = .{ 1, 2, sentinel }; + const slice = arr[0..2 :@intCast(sentinel)]; + + comptime assert(@TypeOf(slice) == *const [2:sentinel]u16); + comptime assert(slice[2] == sentinel); + comptime assert(slice.len == 2); + comptime assert(slice[0] == 1); + comptime assert(slice[1] == 2); +}