Commit e58a0c5e9e

dweiller <4678790+dweiller@users.noreplay.github.com>
2023-04-30 07:00:28
sema: implement slice_length ZIR instruction
1 parent a62b5d8
Changed files (1)
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;