| ... | @@ -23414,18 +23414,29 @@ fn ptrCastFull( | ... | @@ -23414,18 +23414,29 @@ fn ptrCastFull( |
| 23414 | return sema.fail(block, src, "illegal pointer cast to slice", .{}); | 23414 | return sema.fail(block, src, "illegal pointer cast to slice", .{}); |
| 23415 | } | 23415 | } |
| 23416 | | 23416 | |
| 23417 | if (dest_info.flags.size == .slice) { | 23417 | // Only defined if `src_slice_like` |
| 23418 | const src_elem_size = switch (src_info.flags.size) { | 23418 | const src_slice_like_elem: Type = if (src_slice_like) switch (src_info.flags.size) { |
| 23419 | .slice => Type.fromInterned(src_info.child).abiSize(zcu), | 23419 | .slice => .fromInterned(src_info.child), |
| 23420 | // pointer to array | 23420 | // pointer to array |
| 23421 | .one => Type.fromInterned(src_info.child).childType(zcu).abiSize(zcu), | 23421 | .one => Type.fromInterned(src_info.child).childType(zcu), |
| 23422 | else => unreachable, | 23422 | else => unreachable, |
| 23423 | }; | 23423 | } else undefined; |
| 23424 | const dest_elem_size = Type.fromInterned(dest_info.child).abiSize(zcu); | 23424 | |
| 23425 | if (src_elem_size != dest_elem_size) { | 23425 | const slice_needs_len_change: bool = if (dest_info.flags.size == .slice) need_len_change: { |
| 23426 | return sema.fail(block, src, "TODO: implement {s} between slices changing the length", .{operation}); | 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 | // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs | 23441 | // The checking logic in this function must stay in sync with Sema.coerceInMemoryAllowedPtrs |
| 23431 | | 23442 | |
| ... | @@ -23638,155 +23649,300 @@ fn ptrCastFull( | ... | @@ -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: { | 23652 | // Type validation done -- this cast is okay. Let's do it! |
| 23642 | if (operand_ty.zigTypeTag(zcu) == .optional) { | 23653 | // |
| 23643 | break :ptr try sema.analyzeOptionalSlicePtr(block, operand_src, operand, operand_ty); | 23654 | // `operand` is a maybe-optional pointer or slice. |
| 23644 | } else { | 23655 | // `dest_ty` is a maybe-optional pointer or slice. |
| 23645 | break :ptr try sema.analyzeSlicePtr(block, operand_src, operand, operand_ty); | 23656 | // |
| 23646 | } | 23657 | // We have a few safety checks: |
| 23647 | } else operand; | 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: { | 23662 | ct: { |
| 23650 | // Only convert to a many-pointer at first | 23663 | if (flags.addrspace_cast) break :ct; // cannot `@addrSpaceCast` at comptime |
| 23651 | var info = dest_info; | 23664 | const operand_val = try sema.resolveValue(operand) orelse break :ct; |
| 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; | | |
| 23660 | | 23665 | |
| 23661 | // Cannot do @addrSpaceCast at comptime | 23666 | if (operand_val.isUndef(zcu)) { |
| 23662 | if (!flags.addrspace_cast) { | 23667 | if (!dest_ty.ptrAllowsZero(zcu)) { |
| 23663 | if (try sema.resolveValue(ptr)) |ptr_val| { | | |
| 23664 | if (!dest_ty.ptrAllowsZero(zcu) and ptr_val.isUndef(zcu)) { | | |
| 23665 | return sema.failWithUseOfUndef(block, operand_src); | 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 | return sema.fail(block, operand_src, "null pointer casted to type '{}'", .{dest_ty.fmt(pt)}); | 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)) { | 23677 | if (dest_ty.zigTypeTag(zcu) == .optional) { |
| 23671 | if (try ptr_val.getUnsignedIntSema(pt)) |addr| { | 23678 | return Air.internedToRef((try pt.nullValue(dest_ty)).toIntern()); |
| 23672 | if (!dest_align.check(addr)) { | 23679 | } else { |
| 23673 | return sema.fail(block, operand_src, "pointer address 0x{X} is not aligned to {d} bytes", .{ | 23680 | return Air.internedToRef((try pt.ptrIntValue(dest_ty, 0)).toIntern()); |
| 23674 | addr, | 23681 | } |
| 23675 | dest_align.toByteUnits().?, | 23682 | } |
| 23676 | }); | 23683 | |
| 23677 | } | 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) { | 23701 | } |
| 23681 | if (ptr_val.isUndef(zcu)) return pt.undefRef(dest_ty); | 23702 | |
| 23682 | const arr_len = try pt.intValue(Type.usize, Type.fromInterned(src_info.child).arrayLen(zcu)); | 23703 | if (dest_info.flags.size != .slice) { |
| 23683 | const ptr_val_key = zcu.intern_pool.indexToKey(ptr_val.toIntern()).ptr; | 23704 | // Any to non-slice |
| 23684 | return Air.internedToRef((try pt.intern(.{ .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 | .ty = dest_ty.toIntern(), | 23714 | .ty = dest_ty.toIntern(), |
| 23686 | .ptr = try pt.intern(.{ .ptr = .{ | 23715 | .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(), |
| 23687 | .ty = dest_ty.slicePtrFieldType(zcu).toIntern(), | 23716 | .len = len_val.toIntern(), |
| 23688 | .base_addr = ptr_val_key.base_addr, | 23717 | } })); |
| 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()); | | |
| 23696 | } | 23718 | } |
| 23697 | } | 23719 | } |
| 23698 | } | | |
| 23699 | | 23720 | |
| 23700 | try sema.requireRuntimeBlock(block, src, null); | 23721 | // Slice-like to slice, fallback |
| 23701 | try sema.validateRuntimeValue(block, operand_src, ptr); | | |
| 23702 | | 23722 | |
| 23703 | if (block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu) and | 23723 | const src_len: u64 = if (maybe_len_val) |val| |
| 23704 | (try Type.fromInterned(dest_info.child).hasRuntimeBitsSema(pt) or Type.fromInterned(dest_info.child).zigTypeTag(zcu) == .@"fn")) | 23724 | try val.toUnsignedIntSema(pt) |
| 23705 | { | | |
| 23706 | const actual_ptr = if (src_info.flags.size == .slice) | | |
| 23707 | try sema.analyzeSlicePtr(block, src, ptr, operand_ty) | | |
| 23708 | else | 23725 | else |
| 23709 | ptr; | 23726 | Type.fromInterned(src_info.child).arrayLen(zcu); |
| 23710 | const ptr_int = try block.addBitCast(.usize, actual_ptr); | 23727 | |
| 23711 | const is_non_zero = try block.addBinOp(.cmp_neq, ptr_int, .zero_usize); | 23728 | const dest_len: u64 = if (slice_needs_len_change) len: { |
| 23712 | const ok = if (src_info.flags.size == .slice and dest_info.flags.size == .slice) ok: { | 23729 | const src_elem_size = src_slice_like_elem.abiSize(zcu); |
| 23713 | const len = try sema.analyzeSliceLen(block, operand_src, ptr); | 23730 | const dest_elem_size = Type.fromInterned(dest_info.child).abiSize(zcu); |
| 23714 | const len_zero = try block.addBinOp(.cmp_eq, len, .zero_usize); | 23731 | const bytes = src_len * src_elem_size; |
| 23715 | break :ok try block.addBinOp(.bool_or, len_zero, is_non_zero); | 23732 | // Check: element count divides neatly |
| 23716 | } else is_non_zero; | 23733 | break :len std.math.divExact(u64, bytes, dest_elem_size) catch |err| switch (err) { |
| 23717 | try sema.addSafetyCheck(block, src, ok, .cast_to_null); | 23734 | error.DivisionByZero => unreachable, |
| 23718 | } | 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 | 23739 | return Air.internedToRef(try pt.intern(.{ .slice = .{ |
| 23721 | dest_align.compare(.gt, src_align) and | 23740 | .ty = dest_ty.toIntern(), |
| 23722 | try Type.fromInterned(dest_info.child).hasRuntimeBitsSema(pt)) | 23741 | .ptr = (try pt.getCoerced(ptr_val, dest_ty.slicePtrFieldType(zcu))).toIntern(), |
| 23723 | { | 23742 | .len = (try pt.intValue(.usize, dest_len)).toIntern(), |
| 23724 | const align_bytes_minus_1 = dest_align.toByteUnits().? - 1; | 23743 | } })); |
| 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); | | |
| 23745 | } | 23744 | } |
| 23746 | | 23745 | |
| 23747 | // If we're going from an array pointer to a slice, this will only be the pointer part! | 23746 | try sema.validateRuntimeValue(block, operand_src, operand); |
| 23748 | const result_ptr = if (flags.addrspace_cast) ptr: { | 23747 | |
| 23749 | // We can't change address spaces with a bitcast, so this requires two instructions | 23748 | const need_null_check = block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu); |
| 23750 | var intermediate_info = src_info; | 23749 | const need_align_check = block.wantSafety() and dest_align.compare(.gt, src_align); |
| 23751 | intermediate_info.flags.address_space = dest_info.flags.address_space; | 23750 | |
| 23752 | const intermediate_ptr_ty = try pt.ptrTypeSema(intermediate_info); | 23751 | // `operand` might be a slice. If `need_operand_ptr`, we'll populate `operand_ptr` with the raw pointer. |
| 23753 | const intermediate_ty = if (dest_ptr_ty.zigTypeTag(zcu) == .optional) blk: { | 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 | break :blk try pt.optionalType(intermediate_ptr_ty.toIntern()); | 23780 | break :blk try pt.optionalType(intermediate_ptr_ty.toIntern()); |
| 23755 | } else intermediate_ptr_ty; | 23781 | } else intermediate_ptr_ty; |
| 23756 | const intermediate = try block.addInst(.{ | 23782 | break :ptr try block.addInst(.{ |
| 23757 | .tag = .addrspace_cast, | 23783 | .tag = .addrspace_cast, |
| 23758 | .data = .{ .ty_op = .{ | 23784 | .data = .{ .ty_op = .{ |
| 23759 | .ty = Air.internedToRef(intermediate_ty.toIntern()), | 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) { | 23791 | // Whether we need to know if the (slice) operand has `len == 0`. |
| 23773 | // We have to construct a slice using the operand's child's array length | 23792 | const need_operand_len_is_zero = src_info.flags.size == .slice and |
| 23774 | // Note that we know from the check at the start of the function that operand_ty is slice-like | 23793 | dest_info.flags.size == .slice and |
| 23775 | const arr_len = Air.internedToRef((try pt.intValue(Type.usize, Type.fromInterned(src_info.child).arrayLen(zcu))).toIntern()); | 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 | return block.addInst(.{ | 23925 | return block.addInst(.{ |
| 23777 | .tag = .slice, | 23926 | .tag = .slice, |
| 23778 | .data = .{ .ty_pl = .{ | 23927 | .data = .{ .ty_pl = .{ |
| 23779 | .ty = Air.internedToRef(dest_ty.toIntern()), | 23928 | .ty = Air.internedToRef(dest_ty.toIntern()), |
| 23780 | .payload = try sema.addExtra(Air.Bin{ | 23929 | .payload = try sema.addExtra(Air.Bin{ |
| 23781 | .lhs = result_ptr, | 23930 | .lhs = coerced_ptr, |
| 23782 | .rhs = arr_len, | 23931 | .rhs = result_len, |
| 23783 | }), | 23932 | }), |
| 23784 | } }, | 23933 | } }, |
| 23785 | }); | 23934 | }); |
| 23786 | } else { | 23935 | } else { |
| 23787 | assert(dest_ptr_ty.eql(dest_ty, zcu)); | 23936 | assert(need_operand_ptr); |
| 23788 | try sema.checkKnownAllocPtr(block, operand, result_ptr); | 23937 | // We just need to bitcast the pointer, if necessary. |
| 23789 | return result_ptr; | 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,6 +38984,12 @@ fn getExpectedBuiltinFnType(sema: *Sema, decl: Zcu.BuiltinDecl) CompileError!Typ |
| 38828 | .return_type = .noreturn_type, | 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 | // `fn (usize, usize) noreturn` | 38993 | // `fn (usize, usize) noreturn` |
| 38832 | .@"panic.outOfBounds", | 38994 | .@"panic.outOfBounds", |
| 38833 | .@"panic.startGreaterThanEnd", | 38995 | .@"panic.startGreaterThanEnd", |