| ... | ... | @@ -23414,18 +23414,29 @@ fn ptrCastFull( |
| 23414 | 23414 | return sema.fail(block, src, "illegal pointer cast to slice", .{}); |
| 23415 | 23415 | } |
| 23416 | 23416 | |
| 23417 | | if (dest_info.flags.size == .slice) { |
| 23418 | | const src_elem_size = switch (src_info.flags.size) { |
| 23419 | | .slice => Type.fromInterned(src_info.child).abiSize(zcu), |
| 23420 | | // pointer to array |
| 23421 | | .one => Type.fromInterned(src_info.child).childType(zcu).abiSize(zcu), |
| 23422 | | else => unreachable, |
| 23423 | | }; |
| 23424 | | const dest_elem_size = Type.fromInterned(dest_info.child).abiSize(zcu); |
| 23425 | | if (src_elem_size != dest_elem_size) { |
| 23426 | | return sema.fail(block, src, "TODO: implement {s} between slices changing the length", .{operation}); |
| 23417 | // Only defined if `src_slice_like` |
| 23418 | const src_slice_like_elem: Type = if (src_slice_like) switch (src_info.flags.size) { |
| 23419 | .slice => .fromInterned(src_info.child), |
| 23420 | // pointer to array |
| 23421 | .one => Type.fromInterned(src_info.child).childType(zcu), |
| 23422 | else => unreachable, |
| 23423 | } else undefined; |
| 23424 | |
| 23425 | const slice_needs_len_change: bool = if (dest_info.flags.size == .slice) need_len_change: { |
| 23426 | const dest_elem: Type = .fromInterned(dest_info.child); |
| 23427 | if (src_slice_like_elem.toIntern() == dest_elem.toIntern()) { |
| 23428 | break :need_len_change false; |
| 23427 | 23429 | } |
| 23428 | | } |
| 23430 | if (src_slice_like_elem.comptimeOnly(zcu) or dest_elem.comptimeOnly(zcu)) { |
| 23431 | return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) }); |
| 23432 | } |
| 23433 | const src_elem_size = src_slice_like_elem.abiSize(zcu); |
| 23434 | const dest_elem_size = dest_elem.abiSize(zcu); |
| 23435 | if (src_elem_size == 0 or dest_elem_size == 0) { |
| 23436 | return sema.fail(block, src, "cannot infer length of slice of '{}' from slice of '{}'", .{ dest_elem.fmt(pt), src_slice_like_elem.fmt(pt) }); |
| 23437 | } |
| 23438 | break :need_len_change src_elem_size != dest_elem_size; |
| 23439 | } else false; |
| 23429 | 23440 | |
| 23430 | 23441 | // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs |
| 23431 | 23442 | |
| ... | ... | @@ -23638,155 +23649,300 @@ fn ptrCastFull( |
| 23638 | 23649 | } |
| 23639 | 23650 | } |
| 23640 | 23651 | |
| 23641 | | const ptr = if (src_info.flags.size == .slice and dest_info.flags.size != .slice) ptr: { |
| 23642 | | if (operand_ty.zigTypeTag(zcu) == .optional) { |
| 23643 | | break :ptr try sema.analyzeOptionalSlicePtr(block, operand_src, operand, operand_ty); |
| 23644 | | } else { |
| 23645 | | break :ptr try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty); |
| 23646 | | } |
| 23647 | | } else operand; |
| 23652 | // Type validation done -- this cast is okay. Let's do it! |
| 23653 | // |
| 23654 | // `operand` is a maybe-optional pointer or slice. |
| 23655 | // `dest_ty` is a maybe-optional pointer or slice. |
| 23656 | // |
| 23657 | // We have a few safety checks: |
| 23658 | // * if the destination does not allow zero, check the operand is not null / 0 |
| 23659 | // * if the destination is more aligned than the operand, check the pointer alignment |
| 23660 | // * if `slice_needs_len_change`, check the element count divides neatly |
| 23648 | 23661 | |
| 23649 | | const dest_ptr_ty = if (dest_info.flags.size == .slice and src_info.flags.size != .slice) blk: { |
| 23650 | | // Only convert to a many-pointer at first |
| 23651 | | var info = dest_info; |
| 23652 | | info.flags.size = .many; |
| 23653 | | const ty = try pt.ptrTypeSema(info); |
| 23654 | | if (dest_ty.zigTypeTag(zcu) == .optional) { |
| 23655 | | break :blk try pt.optionalType(ty.toIntern()); |
| 23656 | | } else { |
| 23657 | | break :blk ty; |
| 23658 | | } |
| 23659 | | } else dest_ty; |
| 23662 | ct: { |
| 23663 | if (flags.addrspace_cast) break :ct; // cannot `@addrSpaceCast` at comptime |
| 23664 | const operand_val = try sema.resolveValue(operand) orelse break :ct; |
| 23660 | 23665 | |
| 23661 | | // Cannot do @addrSpaceCast at comptime |
| 23662 | | if (!flags.addrspace_cast) { |
| 23663 | | if (try sema.resolveValue(ptr)) |ptr_val| { |
| 23664 | | if (!dest_ty.ptrAllowsZero(zcu) and ptr_val.isUndef(zcu)) { |
| 23666 | if (operand_val.isUndef(zcu)) { |
| 23667 | if (!dest_ty.ptrAllowsZero(zcu)) { |
| 23665 | 23668 | return sema.failWithUseOfUndef(block, operand_src); |
| 23666 | 23669 | } |
| 23667 | | if (!dest_ty.ptrAllowsZero(zcu) and ptr_val.isNull(zcu)) { |
| 23670 | return pt.undefRef(dest_ty); |
| 23671 | } |
| 23672 | |
| 23673 | if (operand_val.isNull(zcu)) { |
| 23674 | if (!dest_ty.ptrAllowsZero(zcu)) { |
| 23668 | 23675 | return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(pt)}); |
| 23669 | 23676 | } |
| 23670 | | if (dest_align.compare(.gt, src_align)) { |
| 23671 | | if (try ptr_val.getUnsignedIntSema(pt)) |addr| { |
| 23672 | | if (!dest_align.check(addr)) { |
| 23673 | | return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ |
| 23674 | | addr, |
| 23675 | | dest_align.toByteUnits().?, |
| 23676 | | }); |
| 23677 | | } |
| 23677 | if (dest_ty.zigTypeTag(zcu) == .optional) { |
| 23678 | return Air.internedToRef((try pt.nullValue(dest_ty)).toIntern()); |
| 23679 | } else { |
| 23680 | return Air.internedToRef((try pt.ptrIntValue(dest_ty, 0)).toIntern()); |
| 23681 | } |
| 23682 | } |
| 23683 | |
| 23684 | const ptr_val: Value, const maybe_len_val: ?Value = switch (src_info.flags.size) { |
| 23685 | .slice => switch (zcu.intern_pool.indexToKey(operand_val.toIntern())) { |
| 23686 | .slice => |slice| .{ .fromInterned(slice.ptr), .fromInterned(slice.len) }, |
| 23687 | else => unreachable, |
| 23688 | }, |
| 23689 | .one, .many, .c => .{ operand_val, null }, |
| 23690 | }; |
| 23691 | |
| 23692 | if (dest_align.compare(.gt, src_align)) { |
| 23693 | if (try ptr_val.getUnsignedIntSema(pt)) |addr| { |
| 23694 | if (!dest_align.check(addr)) { |
| 23695 | return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ |
| 23696 | addr, |
| 23697 | dest_align.toByteUnits().?, |
| 23698 | }); |
| 23678 | 23699 | } |
| 23679 | 23700 | } |
| 23680 | | if (dest_info.flags.size == .slice and src_info.flags.size != .slice) { |
| 23681 | | if (ptr_val.isUndef(zcu)) return pt.undefRef(dest_ty); |
| 23682 | | const arr_len = try pt.intValue(Type.usize, Type.fromInterned(src_info.child).arrayLen(zcu)); |
| 23683 | | const ptr_val_key = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr; |
| 23684 | | return Air.internedToRef((try pt.intern(.{ .slice = .{ |
| 23701 | } |
| 23702 | |
| 23703 | if (dest_info.flags.size != .slice) { |
| 23704 | // Any to non-slice |
| 23705 | const new_ptr_val = try pt.getCoerced(ptr_val, dest_ty); |
| 23706 | return Air.internedToRef(new_ptr_val.toIntern()); |
| 23707 | } |
| 23708 | |
| 23709 | // Slice-like to slice, compatible element type |
| 23710 | // Here, we can preserve a lazy length. |
| 23711 | if (!slice_needs_len_change) { |
| 23712 | if (maybe_len_val) |len_val| { |
| 23713 | return Air.internedToRef(try pt.intern(.{ .slice = .{ |
| 23685 | 23714 | .ty = dest_ty.toIntern(), |
| 23686 | | .ptr = try pt.intern(.{ .ptr = .{ |
| 23687 | | .ty = dest_ty.slicePtrFieldType(zcu).toIntern(), |
| 23688 | | .base_addr = ptr_val_key.base_addr, |
| 23689 | | .byte_offset = ptr_val_key.byte_offset, |
| 23690 | | } }), |
| 23691 | | .len = arr_len.toIntern(), |
| 23692 | | } }))); |
| 23693 | | } else { |
| 23694 | | assert(dest_ptr_ty.eql(dest_ty, zcu)); |
| 23695 | | return Air.internedToRef((try pt.getCoerced(ptr_val, dest_ty)).toIntern()); |
| 23715 | .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(), |
| 23716 | .len = len_val.toIntern(), |
| 23717 | } })); |
| 23696 | 23718 | } |
| 23697 | 23719 | } |
| 23698 | | } |
| 23699 | 23720 | |
| 23700 | | try sema.requireRuntimeBlock(block, src, null); |
| 23701 | | try sema.validateRuntimeValue(block, operand_src, ptr); |
| 23721 | // Slice-like to slice, fallback |
| 23702 | 23722 | |
| 23703 | | if (block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu) and |
| 23704 | | (try Type.fromInterned(dest_info.child).hasRuntimeBitsSema(pt) or Type.fromInterned(dest_info.child).zigTypeTag(zcu) == .@"fn")) |
| 23705 | | { |
| 23706 | | const actual_ptr = if (src_info.flags.size == .slice) |
| 23707 | | try sema.analyzeSlicePtr(block, src, ptr, operand_ty) |
| 23723 | const src_len: u64 = if (maybe_len_val) |val| |
| 23724 | try val.toUnsignedIntSema(pt) |
| 23708 | 23725 | else |
| 23709 | | ptr; |
| 23710 | | const ptr_int = try block.addBitCast(.usize, actual_ptr); |
| 23711 | | const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); |
| 23712 | | const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { |
| 23713 | | const len = try sema.analyzeSliceLen(block, operand_src, ptr); |
| 23714 | | const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); |
| 23715 | | break :ok try block.addBinOp(.bool_or, len_zero, is_non_zero); |
| 23716 | | } else is_non_zero; |
| 23717 | | try sema.addSafetyCheck(block, src, ok, .cast_to_null); |
| 23718 | | } |
| 23726 | Type.fromInterned(src_info.child).arrayLen(zcu); |
| 23727 | |
| 23728 | const dest_len: u64 = if (slice_needs_len_change) len: { |
| 23729 | const src_elem_size = src_slice_like_elem.abiSize(zcu); |
| 23730 | const dest_elem_size = Type.fromInterned(dest_info.child).abiSize(zcu); |
| 23731 | const bytes = src_len * src_elem_size; |
| 23732 | // Check: element count divides neatly |
| 23733 | break :len std.math.divExact(u64, bytes, dest_elem_size) catch |err| switch (err) { |
| 23734 | error.DivisionByZero => unreachable, |
| 23735 | error.UnexpectedRemainder => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), |
| 23736 | }; |
| 23737 | } else src_len; |
| 23719 | 23738 | |
| 23720 | | if (block.wantSafety() and |
| 23721 | | dest_align.compare(.gt, src_align) and |
| 23722 | | try Type.fromInterned(dest_info.child).hasRuntimeBitsSema(pt)) |
| 23723 | | { |
| 23724 | | const align_bytes_minus_1 = dest_align.toByteUnits().? - 1; |
| 23725 | | const align_mask = Air.internedToRef((try pt.intValue( |
| 23726 | | Type.usize, |
| 23727 | | if (Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu)) |mask| |
| 23728 | | align_bytes_minus_1 & mask |
| 23729 | | else |
| 23730 | | align_bytes_minus_1, |
| 23731 | | )).toIntern()); |
| 23732 | | const actual_ptr = if (src_info.flags.size == .slice) |
| 23733 | | try sema.analyzeSlicePtr(block, src, ptr, operand_ty) |
| 23734 | | else |
| 23735 | | ptr; |
| 23736 | | const ptr_int = try block.addBitCast(.usize, actual_ptr); |
| 23737 | | const remainder = try block.addBinOp(.bit_and, ptr_int, align_mask); |
| 23738 | | const is_aligned = try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 23739 | | const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { |
| 23740 | | const len = try sema.analyzeSliceLen(block, operand_src, ptr); |
| 23741 | | const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); |
| 23742 | | break :ok try block.addBinOp(.bool_or, len_zero, is_aligned); |
| 23743 | | } else is_aligned; |
| 23744 | | try sema.addSafetyCheck(block, src, ok, .incorrect_alignment); |
| 23739 | return Air.internedToRef(try pt.intern(.{ .slice = .{ |
| 23740 | .ty = dest_ty.toIntern(), |
| 23741 | .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(), |
| 23742 | .len = (try pt.intValue(.usize, dest_len)).toIntern(), |
| 23743 | } })); |
| 23745 | 23744 | } |
| 23746 | 23745 | |
| 23747 | | // If we're going from an array pointer to a slice, this will only be the pointer part! |
| 23748 | | const result_ptr = if (flags.addrspace_cast) ptr: { |
| 23749 | | // We can't change address spaces with a bitcast, so this requires two instructions |
| 23750 | | var intermediate_info = src_info; |
| 23751 | | intermediate_info.flags.address_space = dest_info.flags.address_space; |
| 23752 | | const intermediate_ptr_ty = try pt.ptrTypeSema(intermediate_info); |
| 23753 | | const intermediate_ty = if (dest_ptr_ty.zigTypeTag(zcu) == .optional) blk: { |
| 23746 | try sema.validateRuntimeValue(block, operand_src, operand); |
| 23747 | |
| 23748 | const need_null_check = block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu); |
| 23749 | const need_align_check = block.wantSafety() and dest_align.compare(.gt, src_align); |
| 23750 | |
| 23751 | // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer. |
| 23752 | const need_operand_ptr = src_info.flags.size != .slice or // we already have it |
| 23753 | dest_info.flags.size != .slice or // the result is a raw pointer |
| 23754 | need_null_check or // safety check happens on pointer |
| 23755 | need_align_check or // safety check happens on pointer |
| 23756 | flags.addrspace_cast or // AIR addrspace_cast acts on a pointer |
| 23757 | slice_needs_len_change; // to change the length, we reconstruct the slice |
| 23758 | |
| 23759 | // This is not quite just the pointer part of `operand` -- it's also had the address space cast done already. |
| 23760 | const operand_ptr: Air.Inst.Ref = ptr: { |
| 23761 | if (!need_operand_ptr) break :ptr .none; |
| 23762 | // First, just get the pointer. |
| 23763 | const pre_addrspace_cast = inner: { |
| 23764 | if (src_info.flags.size != .slice) break :inner operand; |
| 23765 | if (operand_ty.zigTypeTag(zcu) == .optional) { |
| 23766 | break :inner try sema.analyzeOptionalSlicePtr(block, operand_src, operand, operand_ty); |
| 23767 | } else { |
| 23768 | break :inner try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty); |
| 23769 | } |
| 23770 | }; |
| 23771 | // Now, do an addrspace cast if necessary! |
| 23772 | if (!flags.addrspace_cast) break :ptr pre_addrspace_cast; |
| 23773 | |
| 23774 | const intermediate_ptr_ty = try pt.ptrTypeSema(info: { |
| 23775 | var info = src_info; |
| 23776 | info.flags.address_space = dest_info.flags.address_space; |
| 23777 | break :info info; |
| 23778 | }); |
| 23779 | const intermediate_ty = if (operand_ty.zigTypeTag(zcu) == .optional) blk: { |
| 23754 | 23780 | break :blk try pt.optionalType(intermediate_ptr_ty.toIntern()); |
| 23755 | 23781 | } else intermediate_ptr_ty; |
| 23756 | | const intermediate = try block.addInst(.{ |
| 23782 | break :ptr try block.addInst(.{ |
| 23757 | 23783 | .tag = .addrspace_cast, |
| 23758 | 23784 | .data = .{ .ty_op = .{ |
| 23759 | 23785 | .ty = Air.internedToRef(intermediate_ty.toIntern()), |
| 23760 | | .operand = ptr, |
| 23786 | .operand = pre_addrspace_cast, |
| 23761 | 23787 | } }, |
| 23762 | 23788 | }); |
| 23763 | | if (intermediate_ty.eql(dest_ptr_ty, zcu)) { |
| 23764 | | // We only changed the address space, so no need for a bitcast |
| 23765 | | break :ptr intermediate; |
| 23766 | | } |
| 23767 | | break :ptr try block.addBitCast(dest_ptr_ty, intermediate); |
| 23768 | | } else ptr: { |
| 23769 | | break :ptr try block.addBitCast(dest_ptr_ty, ptr); |
| 23770 | 23789 | }; |
| 23771 | 23790 | |
| 23772 | | if (dest_info.flags.size == .slice and src_info.flags.size != .slice) { |
| 23773 | | // We have to construct a slice using the operand's child's array length |
| 23774 | | // Note that we know from the check at the start of the function that operand_ty is slice-like |
| 23775 | | const arr_len = Air.internedToRef((try pt.intValue(Type.usize, Type.fromInterned(src_info.child).arrayLen(zcu))).toIntern()); |
| 23791 | // Whether we need to know if the (slice) operand has `len == 0`. |
| 23792 | const need_operand_len_is_zero = src_info.flags.size == .slice and |
| 23793 | dest_info.flags.size == .slice and |
| 23794 | (need_null_check or need_align_check); |
| 23795 | // Whether we need to get the (slice) operand's `len`. |
| 23796 | const need_operand_len = need_len: { |
| 23797 | if (src_info.flags.size != .slice) break :need_len false; |
| 23798 | if (dest_info.flags.size != .slice) break :need_len false; |
| 23799 | if (need_operand_len_is_zero) break :need_len true; |
| 23800 | if (flags.addrspace_cast or slice_needs_len_change) break :need_len true; |
| 23801 | break :need_len false; |
| 23802 | }; |
| 23803 | // `.none` if `!need_operand_len`. |
| 23804 | const operand_len: Air.Inst.Ref = len: { |
| 23805 | if (!need_operand_len) break :len .none; |
| 23806 | break :len try block.addTyOp(.slice_len, .usize, operand); |
| 23807 | }; |
| 23808 | // `.none` if `!need_operand_len_is_zero`. |
| 23809 | const operand_len_is_zero: Air.Inst.Ref = zero: { |
| 23810 | if (!need_operand_len_is_zero) break :zero .none; |
| 23811 | assert(need_operand_len); |
| 23812 | break :zero try block.addBinOp(.cmp_eq, operand_len, .zero_usize); |
| 23813 | }; |
| 23814 | |
| 23815 | // `operand_ptr` converted to an integer, for safety checks. |
| 23816 | const operand_ptr_int: Air.Inst.Ref = if (need_null_check or need_align_check) i: { |
| 23817 | assert(need_operand_ptr); |
| 23818 | break :i try block.addBitCast(.usize, operand_ptr); |
| 23819 | } else .none; |
| 23820 | |
| 23821 | if (need_null_check) { |
| 23822 | assert(operand_ptr_int != .none); |
| 23823 | const ptr_is_non_zero = try block.addBinOp(.cmp_neq, operand_ptr_int, .zero_usize); |
| 23824 | const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { |
| 23825 | break :ok try block.addBinOp(.bool_or, operand_len_is_zero, ptr_is_non_zero); |
| 23826 | } else ptr_is_non_zero; |
| 23827 | try sema.addSafetyCheck(block, src, ok, .cast_to_null); |
| 23828 | } |
| 23829 | if (need_align_check) { |
| 23830 | assert(operand_ptr_int != .none); |
| 23831 | const align_mask = try pt.intRef(.usize, mask: { |
| 23832 | const target_ptr_mask: u64 = Type.fromInterned(dest_info.child).fnPtrMaskOrNull(zcu) orelse ~@as(u64, 0); |
| 23833 | break :mask (dest_align.toByteUnits().? - 1) & target_ptr_mask; |
| 23834 | }); |
| 23835 | const ptr_masked = try block.addBinOp(.bit_and, operand_ptr_int, align_mask); |
| 23836 | const is_aligned = try block.addBinOp(.cmp_eq, ptr_masked, .zero_usize); |
| 23837 | const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { |
| 23838 | break :ok try block.addBinOp(.bool_or, operand_len_is_zero, is_aligned); |
| 23839 | } else is_aligned; |
| 23840 | try sema.addSafetyCheck(block, src, ok, .incorrect_alignment); |
| 23841 | } |
| 23842 | |
| 23843 | if (dest_info.flags.size == .slice) { |
| 23844 | if (src_info.flags.size == .slice and !flags.addrspace_cast and !slice_needs_len_change) { |
| 23845 | // Fast path: just bitcast! |
| 23846 | return block.addBitCast(dest_ty, operand); |
| 23847 | } |
| 23848 | |
| 23849 | // We need to deconstruct the slice (if applicable) and reconstruct it. |
| 23850 | assert(need_operand_ptr); |
| 23851 | |
| 23852 | const result_len: Air.Inst.Ref = len: { |
| 23853 | if (src_info.flags.size == .slice and !slice_needs_len_change) { |
| 23854 | assert(need_operand_len); |
| 23855 | break :len operand_len; |
| 23856 | } |
| 23857 | |
| 23858 | const src_elem_size = src_slice_like_elem.abiSize(zcu); |
| 23859 | const dest_elem_size = Type.fromInterned(dest_info.child).abiSize(zcu); |
| 23860 | if (src_info.flags.size != .slice) { |
| 23861 | assert(src_slice_like); |
| 23862 | const src_len = Type.fromInterned(src_info.child).arrayLen(zcu); |
| 23863 | const bytes = src_len * src_elem_size; |
| 23864 | const dest_len = std.math.divExact(u64, bytes, dest_elem_size) catch |err| switch (err) { |
| 23865 | error.DivisionByZero => unreachable, |
| 23866 | error.UnexpectedRemainder => return sema.fail(block, src, "slice length '{d}' does not divide exactly into destination elements", .{src_len}), |
| 23867 | }; |
| 23868 | break :len try pt.intRef(.usize, dest_len); |
| 23869 | } |
| 23870 | |
| 23871 | assert(need_operand_len); |
| 23872 | |
| 23873 | // If `src_elem_size * n == dest_elem_size`, then just multiply the length by `n`. |
| 23874 | if (std.math.divExact(u64, src_elem_size, dest_elem_size)) |dest_per_src| { |
| 23875 | const multiplier = try pt.intRef(.usize, dest_per_src); |
| 23876 | break :len try block.addBinOp(.mul, operand_len, multiplier); |
| 23877 | } else |err| switch (err) { |
| 23878 | error.DivisionByZero => unreachable, |
| 23879 | error.UnexpectedRemainder => {}, // fall through to code below |
| 23880 | } |
| 23881 | |
| 23882 | // If `src_elem_size == dest_elem_size * n`, then divide the length by `n`. |
| 23883 | // This incurs a safety check. |
| 23884 | if (std.math.divExact(u64, dest_elem_size, src_elem_size)) |src_per_dest| { |
| 23885 | const divisor = try pt.intRef(.usize, src_per_dest); |
| 23886 | if (block.wantSafety()) { |
| 23887 | // Check that the element count divides neatly. |
| 23888 | const remainder = try block.addBinOp(.rem, operand_len, divisor); |
| 23889 | const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 23890 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); |
| 23891 | } |
| 23892 | break :len try block.addBinOp(.div_exact, operand_len, divisor); |
| 23893 | } else |err| switch (err) { |
| 23894 | error.DivisionByZero => unreachable, |
| 23895 | error.UnexpectedRemainder => {}, // fall through to code below |
| 23896 | } |
| 23897 | |
| 23898 | // Fallback: the elements don't divide easily. |
| 23899 | // We'll multiply up to a byte count, then divide down to a new element count. |
| 23900 | // This incurs a safety check. |
| 23901 | |
| 23902 | const src_elem_size_ref = try pt.intRef(.usize, src_elem_size); |
| 23903 | const dest_elem_size_ref = try pt.intRef(.usize, dest_elem_size); |
| 23904 | |
| 23905 | const byte_count = try block.addBinOp(.mul, operand_len, src_elem_size_ref); |
| 23906 | if (block.wantSafety()) { |
| 23907 | // Check that `byte_count` divides neatly into `dest_elem_size`. |
| 23908 | const remainder = try block.addBinOp(.rem, byte_count, dest_elem_size_ref); |
| 23909 | const ok = try block.addBinOp(.cmp_eq, remainder, .zero_usize); |
| 23910 | try sema.addSafetyCheckCall(block, src, ok, .@"panic.sliceCastLenRemainder", &.{operand_len}); |
| 23911 | } |
| 23912 | break :len try block.addBinOp(.div_exact, byte_count, dest_elem_size_ref); |
| 23913 | }; |
| 23914 | |
| 23915 | const operand_ptr_ty = sema.typeOf(operand_ptr); |
| 23916 | const want_ptr_ty = switch (dest_ty.zigTypeTag(zcu)) { |
| 23917 | .optional => try pt.optionalType(dest_ty.childType(zcu).slicePtrFieldType(zcu).toIntern()), |
| 23918 | .pointer => dest_ty.slicePtrFieldType(zcu), |
| 23919 | else => unreachable, |
| 23920 | }; |
| 23921 | const coerced_ptr = if (operand_ptr_ty.toIntern() != want_ptr_ty.toIntern()) ptr: { |
| 23922 | break :ptr try block.addBitCast(want_ptr_ty, operand_ptr); |
| 23923 | } else operand_ptr; |
| 23924 | |
| 23776 | 23925 | return block.addInst(.{ |
| 23777 | 23926 | .tag = .slice, |
| 23778 | 23927 | .data = .{ .ty_pl = .{ |
| 23779 | 23928 | .ty = Air.internedToRef(dest_ty.toIntern()), |
| 23780 | 23929 | .payload = try sema.addExtra(Air.Bin{ |
| 23781 | | .lhs = result_ptr, |
| 23782 | | .rhs = arr_len, |
| 23930 | .lhs = coerced_ptr, |
| 23931 | .rhs = result_len, |
| 23783 | 23932 | }), |
| 23784 | 23933 | } }, |
| 23785 | 23934 | }); |
| 23786 | 23935 | } else { |
| 23787 | | assert(dest_ptr_ty.eql(dest_ty, zcu)); |
| 23788 | | try sema.checkKnownAllocPtr(block, operand, result_ptr); |
| 23789 | | return result_ptr; |
| 23936 | assert(need_operand_ptr); |
| 23937 | // We just need to bitcast the pointer, if necessary. |
| 23938 | // It might not be necessary, since we might have just needed the `addrspace_cast`. |
| 23939 | const result = if (sema.typeOf(operand_ptr).toIntern() == dest_ty.toIntern()) |
| 23940 | operand_ptr |
| 23941 | else |
| 23942 | try block.addBitCast(dest_ty, operand_ptr); |
| 23943 | |
| 23944 | try sema.checkKnownAllocPtr(block, operand, result); |
| 23945 | return result; |
| 23790 | 23946 | } |
| 23791 | 23947 | } |
| 23792 | 23948 | |
| ... | ... | @@ -38828,6 +38984,12 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ |
| 38828 | 38984 | .return_type = .noreturn_type, |
| 38829 | 38985 | }), |
| 38830 | 38986 | |
| 38987 | // `fn (usize) noreturn` |
| 38988 | .@"panic.sliceCastLenRemainder" => try pt.funcType(.{ |
| 38989 | .param_types = &.{.usize_type}, |
| 38990 | .return_type = .noreturn_type, |
| 38991 | }), |
| 38992 | |
| 38831 | 38993 | // `fn (usize, usize) noreturn` |
| 38832 | 38994 | .@"panic.outOfBounds", |
| 38833 | 38995 | .@"panic.startGreaterThanEnd", |