Commit 64e319f555

dweiller <4678790+dweiller@users.noreplay.github.com>
2023-04-30 15:50:08
add optional sentinel to slice_length ZIR
1 parent 7c8d60e
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);
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,
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());
     }
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)
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,
     };