| author | |
| committer | |
| log | f02d21c8c888b07ead4034562beb1872faee11d6 |
| tree | ddc7fd2ee705b06addbea1e6fed394fa05731631 |
| parent | 587bdfc8e5fdc63911070243c308d182f63a8555 |
4 files changed, 78 insertions(+), 1 deletions(-)
src/Sema.zig+28| ... | ... | @@ -21849,6 +21849,34 @@ fn ptrCastFull( |
| 21849 | 21849 | |
| 21850 | 21850 | try sema.validateRuntimeValue(block, operand_src, operand); |
| 21851 | 21851 | |
| 21852 | if (zcu.getTarget().cpu.arch.isSpirV() and | |
| 21853 | src_info.flags.address_space != .physical_storage_buffer and | |
| 21854 | src_info.flags.address_space == dest_info.flags.address_space and | |
| 21855 | src_info.child != dest_info.child and | |
| 21856 | Type.fromInterned(dest_info.child).hasRuntimeBits(zcu)) | |
| 21857 | { | |
| 21858 | var cur: Type = .fromInterned(src_info.child); | |
| 21859 | while (cur.toIntern() != dest_info.child) { | |
| 21860 | cur = switch (cur.zigTypeTag(zcu)) { | |
| 21861 | .array, .vector => cur.childType(zcu), | |
| 21862 | .@"struct" => if (cur.structFieldOffset(0, zcu) == 0) cur.fieldType(0, zcu) else null, | |
| 21863 | else => null, | |
| 21864 | } orelse return sema.failWithOwnedErrorMsg(block, msg: { | |
| 21865 | const msg = try sema.errMsg(src, "cannot cast pointer '{f}' to '{f}'", .{ | |
| 21866 | operand_ty.fmt(pt), dest_ty.fmt(pt), | |
| 21867 | }); | |
| 21868 | errdefer msg.destroy(sema.gpa); | |
| 21869 | try sema.errNote(src, msg, "'{f}' must appear at offset 0 inside '{f}'", .{ | |
| 21870 | Type.fromInterned(dest_info.child).fmt(pt), Type.fromInterned(src_info.child).fmt(pt), | |
| 21871 | }); | |
| 21872 | try sema.errNote(src, msg, "'{s}' pointers can only reach nested types through a first struct field or an array element", .{ | |
| 21873 | @tagName(src_info.flags.address_space), | |
| 21874 | }); | |
| 21875 | break :msg msg; | |
| 21876 | }); | |
| 21877 | } | |
| 21878 | } | |
| 21879 | ||
| 21852 | 21880 | const can_cast_to_int = !target_util.shouldBlockPointerOps(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu)); |
| 21853 | 21881 | const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu); |
| 21854 | 21882 | const need_align_check = can_cast_to_int and block.wantSafety() and dest_align.compare(.gt, src_align); |
src/codegen/spirv/CodeGen.zig+21-1| ... | ... | @@ -5966,7 +5966,27 @@ fn bitCast( |
| 5966 | 5966 | |
| 5967 | 5967 | if (src_ty.toIntern() == dst_ty.toIntern()) return src_id; |
| 5968 | 5968 | if (src_ty.isPtrAtRuntime(zcu) and dst_ty.isPtrAtRuntime(zcu)) switch (target.os.tag) { |
| 5969 | .vulkan, .opengl => if (src_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) return src_id, | |
| 5969 | .vulkan, .opengl => if (src_ty.ptrAddressSpace(zcu) != .physical_storage_buffer) { | |
| 5970 | const src_child = src_ty.childType(zcu); | |
| 5971 | const dst_child = dst_ty.childType(zcu); | |
| 5972 | if (!dst_child.hasRuntimeBits(zcu)) return src_id; | |
| 5973 | if (src_child.toIntern() == dst_child.toIntern()) return src_id; | |
| 5974 | if (src_ty.ptrInfo(zcu).packed_offset.host_size != 0 or | |
| 5975 | dst_ty.ptrInfo(zcu).packed_offset.host_size != 0) return src_id; | |
| 5976 | ||
| 5977 | var indices: std.ArrayList(u32) = .empty; | |
| 5978 | defer indices.deinit(gpa); | |
| 5979 | var cur = src_child; | |
| 5980 | while (cur.toIntern() != dst_child.toIntern()) : (try indices.append(gpa, 0)) { | |
| 5981 | cur = switch (cur.zigTypeTag(zcu)) { | |
| 5982 | .array, .vector => cur.childType(zcu), | |
| 5983 | .@"struct" => cur.fieldType(0, zcu), | |
| 5984 | else => unreachable, | |
| 5985 | }; | |
| 5986 | } | |
| 5987 | const dst_ty_id = try cg.resolveType(dst_ty, .direct); | |
| 5988 | return try cg.accessChain(dst_ty_id, src_id, indices.items); | |
| 5989 | }, | |
| 5970 | 5990 | else => {}, |
| 5971 | 5991 | }; |
| 5972 | 5992 |
test/behavior/spirv.zig+9| ... | ... | @@ -49,6 +49,15 @@ test "@SpirvType" { |
| 49 | 49 | _ = runtime_array; |
| 50 | 50 | } |
| 51 | 51 | |
| 52 | const InnerStruct = extern struct { x: u32 }; | |
| 53 | const OuterStruct = extern struct { inner: InnerStruct, y: u32 }; | |
| 54 | const outer_pc = @extern(*addrspace(.push_constant) const OuterStruct, .{ .name = "outer_pc" }); | |
| 55 | ||
| 56 | test "@ptrCast to first field type" { | |
| 57 | const pc_inner: *addrspace(.push_constant) const InnerStruct = @ptrCast(outer_pc); | |
| 58 | _ = pc_inner; | |
| 59 | } | |
| 60 | ||
| 52 | 61 | test "@SpirvType equality" { |
| 53 | 62 | try expect(@SpirvType(.sampler) == Sampler); |
| 54 | 63 | try expect(@SpirvType(.{ .runtime_array = u32 }) == RuntimeArray); |
test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig created+20| ... | ... | @@ -0,0 +1,20 @@ |
| 1 | const A = extern struct { x: u32, y: u32 }; | |
| 2 | const B = extern struct { a: u64 }; | |
| 3 | ||
| 4 | const a = @extern(*addrspace(.uniform) const A, .{ | |
| 5 | .name = "a", | |
| 6 | .decoration = .{ .descriptor = .{ .set = 0, .binding = 0 } }, | |
| 7 | }); | |
| 8 | ||
| 9 | export fn main() callconv(.{ .spirv_kernel = .{ .x = 1, .y = 1, .z = 1 } }) void { | |
| 10 | const b: *addrspace(.uniform) const B = @ptrCast(a); | |
| 11 | _ = &b; | |
| 12 | } | |
| 13 | ||
| 14 | // error | |
| 15 | // backend=selfhosted | |
| 16 | // target=spirv32-vulkan | |
| 17 | // | |
| 18 | // :10:44: error: cannot cast pointer '*addrspace(.uniform) const A' to '*addrspace(.uniform) const B' | |
| 19 | // :10:44: note: 'B' must appear at offset 0 inside 'A' | |
| 20 | // :10:44: note: 'uniform' pointers can only reach nested types through a first struct field or an array element |