From edb5e493e6d9a4478b3d9c06aa590694757d8c03 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 21 Apr 2023 18:03:33 -0700 Subject: [PATCH] update `@memcpy` to require equal src and dest lens * Sema: upgrade operands to array pointers if possible when emitting AIR. * Implement safety checks for length mismatch and aliasing. * AIR: make ptrtoint support slice operands. Implement in LLVM backend. * C backend: implement new `@memset` semantics. `@memcpy` is not done yet. --- doc/langref.html.in | 26 +++--- lib/std/builtin.zig | 2 + lib/std/crypto/aegis.zig | 4 +- lib/std/hash/murmur.zig | 11 ++- lib/std/mem/Allocator.zig | 3 +- src/Air.zig | 1 + src/AstGen.zig | 2 +- src/Liveness/Verify.zig | 9 +- src/Sema.zig | 175 ++++++++++++++++++++++++++++++++++---- src/codegen/c.zig | 98 +++++++++++++++------ src/codegen/llvm.zig | 12 +-- src/type.zig | 15 ++-- 12 files changed, 280 insertions(+), 78 deletions(-) diff --git a/doc/langref.html.in b/doc/langref.html.in index 7bb60aa348..1fc6a7e98d 100644 --- a/doc/langref.html.in +++ b/doc/langref.html.in @@ -8683,18 +8683,20 @@ test "integer cast panic" { {#header_open|@memcpy#}
{#syntax#}@memcpy(noalias dest, noalias source) void{#endsyntax#}

This function copies bytes from one region of memory to another.

-

{#syntax#}dest{#endsyntax#} must be a mutable slice, or a mutable pointer to an array. - It may have any alignment, and it may have any element type.

-

{#syntax#}source{#endsyntax#} must be an array, pointer, or a slice - with the same element type as {#syntax#}dest{#endsyntax#}. It may have - any alignment. Only {#syntax#}const{#endsyntax#} access is required. It - is sliced from 0 to the same length as - {#syntax#}dest{#endsyntax#}, triggering the same set of safety checks and - possible compile errors as - {#syntax#}source[0..dest.len]{#endsyntax#}.

-

It is illegal for {#syntax#}dest{#endsyntax#} and - {#syntax#}source[0..dest.len]{#endsyntax#} to overlap. If safety - checks are enabled, there will be a runtime check for such overlapping.

+

{#syntax#}dest{#endsyntax#} must be a mutable slice, a mutable pointer to an array, or + a mutable many-item {#link|pointer|Pointer#}. It may have any + alignment, and it may have any element type.

+

Likewise, {#syntax#}source{#endsyntax#} must be a mutable slice, a + mutable pointer to an array, or a mutable many-item + {#link|pointer|Pointer#}. It may have any alignment, and it may have any + element type.

+

The {#syntax#}source{#endsyntax#} element type must support {#link|Type Coercion#} + into the {#syntax#}dest{#endsyntax#} element type. The element types may have + different ABI size, however, that may incur a performance penalty.

+

Similar to {#link|for#} loops, at least one of {#syntax#}source{#endsyntax#} and + {#syntax#}dest{#endsyntax#} must provide a length, and if two lengths are provided, + they must be equal.

+

Finally, the two memory regions must not overlap.

{#header_close#} {#header_open|@memset#} diff --git a/lib/std/builtin.zig b/lib/std/builtin.zig index cfe22099cb..048f73482e 100644 --- a/lib/std/builtin.zig +++ b/lib/std/builtin.zig @@ -1002,6 +1002,8 @@ pub const panic_messages = struct { pub const index_out_of_bounds = "index out of bounds"; pub const start_index_greater_than_end = "start index is larger than end index"; pub const for_len_mismatch = "for loop over objects with non-equal lengths"; + pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths"; + pub const memcpy_alias = "@memcpy arguments alias"; }; pub noinline fn returnError(st: *StackTrace) void { diff --git a/lib/std/crypto/aegis.zig b/lib/std/crypto/aegis.zig index 8cc5a8320e..d7305b444a 100644 --- a/lib/std/crypto/aegis.zig +++ b/lib/std/crypto/aegis.zig @@ -209,7 +209,7 @@ fn Aegis128LGeneric(comptime tag_bits: u9) type { acc |= (computed_tag[j] ^ tag[j]); } if (acc != 0) { - @memset(m.ptr, undefined, m.len); + @memset(m, undefined); return error.AuthenticationFailed; } } @@ -390,7 +390,7 @@ fn Aegis256Generic(comptime tag_bits: u9) type { acc |= (computed_tag[j] ^ tag[j]); } if (acc != 0) { - @memset(m.ptr, undefined, m.len); + @memset(m, undefined); return error.AuthenticationFailed; } } diff --git a/lib/std/hash/murmur.zig b/lib/std/hash/murmur.zig index 15fe1b5d7d..95f7c60efe 100644 --- a/lib/std/hash/murmur.zig +++ b/lib/std/hash/murmur.zig @@ -99,9 +99,8 @@ pub const Murmur2_64 = struct { pub fn hashWithSeed(str: []const u8, seed: u64) u64 { const m: u64 = 0xc6a4a7935bd1e995; - const len = @as(u64, str.len); - var h1: u64 = seed ^ (len *% m); - for (@ptrCast([*]align(1) const u64, str.ptr)[0..@intCast(usize, len >> 3)]) |v| { + var h1: u64 = seed ^ (@as(u64, str.len) *% m); + for (@ptrCast([*]align(1) const u64, str.ptr)[0..str.len / 8]) |v| { var k1: u64 = v; if (native_endian == .Big) k1 = @byteSwap(k1); @@ -111,11 +110,11 @@ pub const Murmur2_64 = struct { h1 ^= k1; h1 *%= m; } - const rest = len & 7; - const offset = len - rest; + const rest = str.len & 7; + const offset = str.len - rest; if (rest > 0) { var k1: u64 = 0; - @memcpy(@ptrCast([*]u8, &k1)[0..@intCast(usize, rest)], @ptrCast([*]const u8, &str[@intCast(usize, offset)])); + @memcpy(@ptrCast([*]u8, &k1)[0..rest], str[offset..]); if (native_endian == .Big) k1 = @byteSwap(k1); h1 ^= k1; diff --git a/lib/std/mem/Allocator.zig b/lib/std/mem/Allocator.zig index ce09cd8baa..b402fab3fa 100644 --- a/lib/std/mem/Allocator.zig +++ b/lib/std/mem/Allocator.zig @@ -282,7 +282,8 @@ pub fn reallocAdvanced( const new_mem = self.rawAlloc(byte_count, log2a(Slice.alignment), return_address) orelse return error.OutOfMemory; - @memcpy(new_mem[0..@min(byte_count, old_byte_slice.len)], old_byte_slice); + const copy_len = @min(byte_count, old_byte_slice.len); + @memcpy(new_mem[0..copy_len], old_byte_slice[0..copy_len]); // TODO https://github.com/ziglang/zig/issues/4298 @memset(old_byte_slice, undefined); self.rawFree(old_byte_slice, log2a(Slice.alignment), return_address); diff --git a/src/Air.zig b/src/Air.zig index c536053f55..4b5d168549 100644 --- a/src/Air.zig +++ b/src/Air.zig @@ -462,6 +462,7 @@ pub const Inst = struct { /// Uses the `ty_op` field. load, /// Converts a pointer to its address. Result type is always `usize`. + /// Pointer type size may be any, including slice. /// Uses the `un_op` field. ptrtoint, /// Given a boolean, returns 0 or 1. diff --git a/src/AstGen.zig b/src/AstGen.zig index 36c03e9ed4..6c9972bc95 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -8455,7 +8455,7 @@ fn builtinCall( .memcpy => { _ = try gz.addPlNode(.memcpy, node, Zir.Inst.Bin{ .lhs = try expr(gz, scope, .{ .rl = .none }, params[0]), - .rhs = try expr(gz, scope, .{ .rl = .ref }, params[1]), + .rhs = try expr(gz, scope, .{ .rl = .none }, params[1]), }); return rvalue(gz, ri, .void_value, node); }, diff --git a/src/Liveness/Verify.zig b/src/Liveness/Verify.zig index 6c1e72392d..3a00492f4b 100644 --- a/src/Liveness/Verify.zig +++ b/src/Liveness/Verify.zig @@ -254,6 +254,8 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { .set_union_tag, .min, .max, + .memset, + .memcpy, => { const bin_op = data[inst].bin_op; try self.verifyInst(inst, .{ bin_op.lhs, bin_op.rhs, .none }); @@ -306,13 +308,6 @@ fn verifyBody(self: *Verify, body: []const Air.Inst.Index) Error!void { const extra = self.air.extraData(Air.Bin, vector_store_elem.payload).data; try self.verifyInst(inst, .{ vector_store_elem.vector_ptr, extra.lhs, extra.rhs }); }, - .memset, - .memcpy, - => { - const pl_op = data[inst].pl_op; - const extra = self.air.extraData(Air.Bin, pl_op.payload).data; - try self.verifyInst(inst, .{ pl_op.operand, extra.lhs, extra.rhs }); - }, .cmpxchg_strong, .cmpxchg_weak, => { diff --git a/src/Sema.zig b/src/Sema.zig index 46798cde57..1b0ca80f18 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -3386,17 +3386,39 @@ fn zirIndexablePtrLen(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileE const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const object = try sema.resolveInst(inst_data.operand); + + return indexablePtrLen(sema, block, src, object); +} + +fn indexablePtrLen( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + object: Air.Inst.Ref, +) CompileError!Air.Inst.Ref { const object_ty = sema.typeOf(object); - const is_pointer_to = object_ty.isSinglePointer(); - - const array_ty = if (is_pointer_to) - object_ty.childType() - else - object_ty; - + const array_ty = if (is_pointer_to) object_ty.childType() else object_ty; try checkIndexable(sema, block, src, array_ty); + return sema.fieldVal(block, src, object, "len", src); +} +fn indexablePtrLenOrNone( + sema: *Sema, + block: *Block, + src: LazySrcLoc, + object: Air.Inst.Ref, +) CompileError!Air.Inst.Ref { + const object_ty = sema.typeOf(object); + const array_ty = t: { + const ptr_size = object_ty.ptrSizeOrNull() orelse break :t object_ty; + break :t switch (ptr_size) { + .Many => return .none, + .One => object_ty.childType(), + else => object_ty, + }; + }; + try checkIndexable(sema, block, src, array_ty); return sema.fieldVal(block, src, object, "len", src); } @@ -21773,6 +21795,29 @@ fn analyzeMinMax( return block.addBinOp(air_tag, simd_op.lhs, simd_op.rhs); } +fn upgradeToArrayPtr(sema: *Sema, block: *Block, ptr: Air.Inst.Ref, len: u64) !Air.Inst.Ref { + const mod = sema.mod; + const info = sema.typeOf(ptr).ptrInfo().data; + if (info.size == .One) { + // Already an array pointer. + return ptr; + } + const new_ty = try Type.ptr(sema.arena, mod, .{ + .pointee_type = try Type.array(sema.arena, len, info.sentinel, info.pointee_type, mod), + .sentinel = null, + .@"align" = info.@"align", + .@"addrspace" = info.@"addrspace", + .mutable = info.mutable, + .@"allowzero" = info.@"allowzero", + .@"volatile" = info.@"volatile", + .size = .One, + }); + if (info.size == .Slice) { + return block.addTyOp(.slice_ptr, new_ty, ptr); + } + return block.addBitCast(new_ty, ptr); +} + fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void { const inst_data = sema.code.instructions.items(.data)[inst].pl_node; const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; @@ -21780,27 +21825,125 @@ fn zirMemcpy(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!void const dest_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; const src_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; const dest_ptr = try sema.resolveInst(extra.lhs); - const src_ptr_ptr = try sema.resolveInst(extra.rhs); - const dest_ptr_ty = sema.typeOf(dest_ptr); - try checkSliceOrArrayType(sema, block, dest_src, dest_ptr_ty); + const src_ptr = try sema.resolveInst(extra.rhs); + const dest_len = try indexablePtrLenOrNone(sema, block, dest_src, dest_ptr); + const src_len = try indexablePtrLenOrNone(sema, block, src_src, src_ptr); - const dest_len = try sema.fieldVal(block, dest_src, dest_ptr, "len", dest_src); - const src_ptr = try sema.analyzeSlice(block, src_src, src_ptr_ptr, .zero_usize, dest_len, .none, .unneeded, src_src, src_src, src_src); + if (dest_len == .none and src_len == .none) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "unknown @memcpy length", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, dest_src, msg, "destination type {} provides no length", .{ + sema.typeOf(dest_ptr).fmt(sema.mod), + }); + try sema.errNote(block, src_src, msg, "source type {} provides no length", .{ + sema.typeOf(src_ptr).fmt(sema.mod), + }); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + + var len_val: ?Value = null; + + if (dest_len != .none and src_len != .none) check: { + // If we can check at compile-time, no need for runtime safety. + if (try sema.resolveDefinedValue(block, dest_src, dest_len)) |dest_len_val| { + len_val = dest_len_val; + if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { + if (!(try sema.valuesEqual(dest_len_val, src_len_val, Type.usize))) { + const msg = msg: { + const msg = try sema.errMsg(block, src, "non-matching @memcpy lengths", .{}); + errdefer msg.destroy(sema.gpa); + try sema.errNote(block, dest_src, msg, "length {} here", .{ + dest_len_val.fmtValue(Type.usize, sema.mod), + }); + try sema.errNote(block, src_src, msg, "length {} here", .{ + src_len_val.fmtValue(Type.usize, sema.mod), + }); + break :msg msg; + }; + return sema.failWithOwnedErrorMsg(msg); + } + break :check; + } + } else if (try sema.resolveDefinedValue(block, src_src, src_len)) |src_len_val| { + len_val = src_len_val; + } + + if (block.wantSafety()) { + const ok = try block.addBinOp(.cmp_eq, dest_len, src_len); + try sema.addSafetyCheck(block, ok, .memcpy_len_mismatch); + } + } const runtime_src = if (try sema.resolveDefinedValue(block, dest_src, dest_ptr)) |dest_ptr_val| rs: { if (!dest_ptr_val.isComptimeMutablePtr()) break :rs dest_src; if (try sema.resolveDefinedValue(block, src_src, src_ptr)) |src_ptr_val| { - if (!src_ptr_val.isComptimeMutablePtr()) break :rs src_src; + _ = src_ptr_val; return sema.fail(block, src, "TODO: @memcpy at comptime", .{}); } else break :rs src_src; } else dest_src; try sema.requireRuntimeBlock(block, src, runtime_src); + + const dest_ty = sema.typeOf(dest_ptr); + const src_ty = sema.typeOf(src_ptr); + + // If in-memory coercion is not allowed, explode this memcpy call into a + // for loop that copies element-wise. + // Likewise if this is an iterable rather than a pointer, do the same + // lowering. The AIR instruction requires pointers with element types of + // equal ABI size. + + if (dest_ty.zigTypeTag() != .Pointer or src_ty.zigTypeTag() != .Pointer) { + return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the source or destination iterable is a tuple", .{}); + } + + const dest_elem_ty = dest_ty.elemType2(); + const src_elem_ty = src_ty.elemType2(); + const target = sema.mod.getTarget(); + if (.ok != try sema.coerceInMemoryAllowed(block, dest_elem_ty, src_elem_ty, true, target, dest_src, src_src)) { + return sema.fail(block, src, "TODO: lower @memcpy to a for loop because the element types have different ABI sizes", .{}); + } + + // If the length is comptime-known, then upgrade src and destination types + // into pointer-to-array. At this point we know they are both pointers + // already. + var new_dest_ptr = dest_ptr; + var new_src_ptr = src_ptr; + if (len_val) |val| { + const len = val.toUnsignedInt(target); + new_dest_ptr = try upgradeToArrayPtr(sema, block, dest_ptr, len); + new_src_ptr = try upgradeToArrayPtr(sema, block, src_ptr, len); + } + + // Aliasing safety check. + if (block.wantSafety()) { + const dest_int = try block.addUnOp(.ptrtoint, new_dest_ptr); + const src_int = try block.addUnOp(.ptrtoint, new_src_ptr); + const len = if (len_val) |v| + try sema.addConstant(Type.usize, v) + else if (dest_len != .none) + dest_len + else + src_len; + + // ok1: dest >= src + len + // ok2: src >= dest + len + const src_plus_len = try block.addBinOp(.add, src_int, len); + const dest_plus_len = try block.addBinOp(.add, dest_int, len); + const ok1 = try block.addBinOp(.cmp_gte, dest_int, src_plus_len); + const ok2 = try block.addBinOp(.cmp_gte, src_int, dest_plus_len); + const ok = try block.addBinOp(.bit_or, ok1, ok2); + try sema.addSafetyCheck(block, ok, .memcpy_alias); + } + _ = try block.addInst(.{ .tag = .memcpy, .data = .{ .bin_op = .{ - .lhs = dest_ptr, - .rhs = src_ptr, + .lhs = new_dest_ptr, + .rhs = new_src_ptr, } }, }); } @@ -22949,6 +23092,8 @@ pub const PanicId = enum { index_out_of_bounds, start_index_greater_than_end, for_len_mismatch, + memcpy_len_mismatch, + memcpy_alias, }; fn addSafetyCheck( diff --git a/src/codegen/c.zig b/src/codegen/c.zig index 903d2449fd..9dac87f11b 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -6177,18 +6177,43 @@ fn airAtomicStore(f: *Function, inst: Air.Inst.Index, order: [*:0]const u8) !CVa } fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { - const pl_op = f.air.instructions.items(.data)[inst].pl_op; - const extra = f.air.extraData(Air.Bin, pl_op.payload).data; - const dest_ty = f.air.typeOf(pl_op.operand); - const dest_ptr = try f.resolveInst(pl_op.operand); - const value = try f.resolveInst(extra.lhs); - const len = try f.resolveInst(extra.rhs); - + const bin_op = f.air.instructions.items(.data)[inst].bin_op; + const dest_ty = f.air.typeOf(bin_op.lhs); + const dest_slice = try f.resolveInst(bin_op.lhs); + const value = try f.resolveInst(bin_op.rhs); + const elem_ty = f.air.typeOf(bin_op.rhs); + const target = f.object.dg.module.getTarget(); + const elem_abi_size = elem_ty.abiSize(target); + const val_is_undef = if (f.air.value(bin_op.rhs)) |val| val.isUndefDeep() else false; const writer = f.object.writer(); - if (dest_ty.isVolatilePtr()) { - var u8_ptr_pl = dest_ty.ptrInfo(); - u8_ptr_pl.data.pointee_type = Type.u8; - const u8_ptr_ty = Type.initPayload(&u8_ptr_pl.base); + + if (val_is_undef) { + try writer.writeAll("memset("); + switch (dest_ty.ptrSize()) { + .Slice => { + try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }); + try writer.writeAll(", 0xaa, "); + try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); + if (elem_abi_size > 1) { + try writer.print(" * {d});\n", .{elem_abi_size}); + } else { + try writer.writeAll(");\n"); + } + }, + .One => { + const array_ty = dest_ty.childType(); + const len = array_ty.arrayLen() * elem_abi_size; + + try f.writeCValue(writer, dest_slice, .FunctionArgument); + try writer.print(", 0xaa, {d});\n", .{len}); + }, + .Many, .C => unreachable, + } + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); + return .none; + } + + if (elem_abi_size > 1 or dest_ty.isVolatilePtr()) { const index = try f.allocLocal(inst, Type.usize); try writer.writeAll("for ("); @@ -6198,36 +6223,61 @@ fn airMemset(f: *Function, inst: Air.Inst.Index) !CValue { try writer.writeAll("; "); try f.writeCValue(writer, index, .Other); try writer.writeAll(" != "); - try f.writeCValue(writer, len, .Other); + switch (dest_ty.ptrSize()) { + .Slice => { + try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); + }, + .One => { + const array_ty = dest_ty.childType(); + const len = array_ty.arrayLen() * elem_abi_size; + try writer.print("{d}", .{len}); + }, + .Many, .C => unreachable, + } try writer.writeAll("; "); try f.writeCValue(writer, index, .Other); try writer.writeAll(" += "); try f.object.dg.renderValue(writer, Type.usize, Value.one, .Other); - try writer.writeAll(") (("); - try f.renderType(writer, u8_ptr_ty); - try writer.writeByte(')'); - try f.writeCValue(writer, dest_ptr, .FunctionArgument); + try writer.writeAll(") ("); + switch (dest_ty.ptrSize()) { + .Slice => try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }), + .One => try f.writeCValue(writer, dest_slice, .FunctionArgument), + .Many, .C => unreachable, + } try writer.writeAll(")["); try f.writeCValue(writer, index, .Other); try writer.writeAll("] = "); try f.writeCValue(writer, value, .FunctionArgument); try writer.writeAll(";\n"); - try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); try freeLocal(f, inst, index.new_local, 0); return .none; } - try reap(f, inst, &.{ pl_op.operand, extra.lhs, extra.rhs }); try writer.writeAll("memset("); - try f.writeCValue(writer, dest_ptr, .FunctionArgument); - try writer.writeAll(", "); - try f.writeCValue(writer, value, .FunctionArgument); - try writer.writeAll(", "); - try f.writeCValue(writer, len, .FunctionArgument); - try writer.writeAll(");\n"); + switch (dest_ty.ptrSize()) { + .Slice => { + try f.writeCValueMember(writer, dest_slice, .{ .identifier = "ptr" }); + try writer.writeAll(", "); + try f.writeCValue(writer, value, .FunctionArgument); + try writer.writeAll(", "); + try f.writeCValueMember(writer, dest_slice, .{ .identifier = "len" }); + try writer.writeAll(");\n"); + }, + .One => { + const array_ty = dest_ty.childType(); + const len = array_ty.arrayLen() * elem_abi_size; + try f.writeCValue(writer, dest_slice, .FunctionArgument); + try writer.writeAll(", "); + try f.writeCValue(writer, value, .FunctionArgument); + try writer.print(", {d});\n", .{len}); + }, + .Many, .C => unreachable, + } + try reap(f, inst, &.{ bin_op.lhs, bin_op.rhs }); return .none; } diff --git a/src/codegen/llvm.zig b/src/codegen/llvm.zig index d80fb62969..50db9c76f8 100644 --- a/src/codegen/llvm.zig +++ b/src/codegen/llvm.zig @@ -5777,10 +5777,10 @@ pub const FuncGen = struct { } fn sliceOrArrayPtr(fg: *FuncGen, ptr: *llvm.Value, ty: Type) *llvm.Value { - switch (ty.ptrSize()) { - .Slice => return fg.builder.buildExtractValue(ptr, 0, ""), - .One => return ptr, - .Many, .C => unreachable, + if (ty.isSlice()) { + return fg.builder.buildExtractValue(ptr, 0, ""); + } else { + return ptr; } } @@ -7917,8 +7917,10 @@ pub const FuncGen = struct { fn airPtrToInt(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); + const ptr_ty = self.air.typeOf(un_op); + const operand_ptr = self.sliceOrArrayPtr(operand, ptr_ty); const dest_llvm_ty = try self.dg.lowerType(self.air.typeOfIndex(inst)); - return self.builder.buildPtrToInt(operand, dest_llvm_ty, ""); + return self.builder.buildPtrToInt(operand_ptr, dest_llvm_ty, ""); } fn airBitCast(self: *FuncGen, inst: Air.Inst.Index) !?*llvm.Value { diff --git a/src/type.zig b/src/type.zig index 00bbcac42f..c7b2844970 100644 --- a/src/type.zig +++ b/src/type.zig @@ -3843,9 +3843,14 @@ pub const Type = extern union { }; } - /// Asserts the `Type` is a pointer. - pub fn ptrSize(self: Type) std.builtin.Type.Pointer.Size { - return switch (self.tag()) { + /// Asserts `ty` is a pointer. + pub fn ptrSize(ty: Type) std.builtin.Type.Pointer.Size { + return ptrSizeOrNull(ty).?; + } + + /// Returns `null` if `ty` is not a pointer. + pub fn ptrSizeOrNull(ty: Type) ?std.builtin.Type.Pointer.Size { + return switch (ty.tag()) { .const_slice, .mut_slice, .const_slice_u8, @@ -3870,9 +3875,9 @@ pub const Type = extern union { .inferred_alloc_mut, => .One, - .pointer => self.castTag(.pointer).?.data.size, + .pointer => ty.castTag(.pointer).?.data.size, - else => unreachable, + else => null, }; }