diff --git a/src/Sema.zig b/src/Sema.zig index d225a8f7b3..bcb13ec6f0 100644 --- a/src/Sema.zig +++ b/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( + 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, - 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) }, - ), - }; + 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); - 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; + 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) }, + ), }; - return sema.failWithOwnedErrorMsg(msg); + + 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); + } } } } - } - 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;