| author | |
| committer | |
| log | e8e47b3428845a176eb5ee1c373063102ab7f6e2 |
| tree | b1de66ba4eeba3f18412e9e95148f45a04aab8e5 |
| parent | 591b2a47f79fa07e87d3b625320135de8446a274 |
5 files changed, 131 insertions(+), 60 deletions(-)
src/Sema.zig+52-34| ... | ... | @@ -22151,34 +22151,7 @@ fn ptrCastFull( |
| 22151 | 22151 | } |
| 22152 | 22152 | |
| 22153 | 22153 | try sema.validateRuntimeValue(block, operand_src, operand); |
| 22154 | ||
| 22155 | if (zcu.getTarget().cpu.arch.isSpirV() and | |
| 22156 | src_info.flags.address_space != .physical_storage_buffer and | |
| 22157 | src_info.flags.address_space == dest_info.flags.address_space and | |
| 22158 | src_info.child != dest_info.child and | |
| 22159 | Type.fromInterned(dest_info.child).hasRuntimeBits(zcu)) | |
| 22160 | { | |
| 22161 | var cur: Type = .fromInterned(src_info.child); | |
| 22162 | while (cur.toIntern() != dest_info.child) { | |
| 22163 | cur = switch (cur.zigTypeTag(zcu)) { | |
| 22164 | .array, .vector => cur.childType(zcu), | |
| 22165 | .@"struct" => if (cur.structFieldOffset(0, zcu) == 0) cur.fieldType(0, zcu) else null, | |
| 22166 | else => null, | |
| 22167 | } orelse return sema.failWithOwnedErrorMsg(block, msg: { | |
| 22168 | const msg = try sema.errMsg(src, "cannot cast pointer '{f}' to '{f}'", .{ | |
| 22169 | operand_ty.fmt(pt), dest_ty.fmt(pt), | |
| 22170 | }); | |
| 22171 | errdefer msg.destroy(sema.gpa); | |
| 22172 | try sema.errNote(src, msg, "'{f}' must appear at offset 0 inside '{f}'", .{ | |
| 22173 | Type.fromInterned(dest_info.child).fmt(pt), Type.fromInterned(src_info.child).fmt(pt), | |
| 22174 | }); | |
| 22175 | try sema.errNote(src, msg, "'{s}' pointers can only reach nested types through a first struct field or an array element", .{ | |
| 22176 | @tagName(src_info.flags.address_space), | |
| 22177 | }); | |
| 22178 | break :msg msg; | |
| 22179 | }); | |
| 22180 | } | |
| 22181 | } | |
| 22154 | try sema.checkLogicalPtrCast(block, src, operand_ty, dest_ty); | |
| 22182 | 22155 | |
| 22183 | 22156 | const can_cast_to_int = !target_util.shouldBlockPointerOps(zcu.getTarget(), operand_ty.ptrAddressSpace(zcu)); |
| 22184 | 22157 | const need_null_check = can_cast_to_int and block.wantSafety() and operand_ty.ptrAllowsZero(zcu) and !dest_ty.ptrAllowsZero(zcu); |
| ... | ... | @@ -22721,6 +22694,8 @@ fn checkPtrType( |
| 22721 | 22694 | fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Type) !void { |
| 22722 | 22695 | const pt = sema.pt; |
| 22723 | 22696 | const zcu = pt.zcu; |
| 22697 | ||
| 22698 | if (block.isComptime() or block.is_typeof) return; | |
| 22724 | 22699 | if (zcu.intern_pool.indexToKey(ty.toIntern()) == .ptr_type) { |
| 22725 | 22700 | const target = zcu.getTarget(); |
| 22726 | 22701 | const as = ty.ptrAddressSpace(zcu); |
| ... | ... | @@ -22731,12 +22706,8 @@ fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ |
| 22731 | 22706 | try sema.errNote( |
| 22732 | 22707 | src, |
| 22733 | 22708 | msg, |
| 22734 | "cannot perform arithmetic on pointers with address space '{s}' on target {s}-{s}", | |
| 22735 | .{ | |
| 22736 | @tagName(as), | |
| 22737 | @tagName(target.cpu.arch.family()), | |
| 22738 | @tagName(target.os.tag), | |
| 22739 | }, | |
| 22709 | "pointers with address space '{t}' do not support arithmetic or indexing on target {t}-{t}", | |
| 22710 | .{ as, target.cpu.arch.family(), target.os.tag }, | |
| 22740 | 22711 | ); |
| 22741 | 22712 | break :msg msg; |
| 22742 | 22713 | }); |
| ... | ... | @@ -22744,6 +22715,49 @@ fn checkLogicalPtrOperation(sema: *Sema, block: *Block, src: LazySrcLoc, ty: Typ |
| 22744 | 22715 | } |
| 22745 | 22716 | } |
| 22746 | 22717 | |
| 22718 | fn checkLogicalPtrCast( | |
| 22719 | sema: *Sema, | |
| 22720 | block: *Block, | |
| 22721 | src: LazySrcLoc, | |
| 22722 | operand_ty: Type, | |
| 22723 | dest_ty: Type, | |
| 22724 | ) CompileError!void { | |
| 22725 | const pt = sema.pt; | |
| 22726 | const zcu = pt.zcu; | |
| 22727 | const src_info = operand_ty.ptrInfo(zcu); | |
| 22728 | const dest_info = dest_ty.ptrInfo(zcu); | |
| 22729 | ||
| 22730 | if (block.isComptime() or block.is_typeof) return; | |
| 22731 | switch (zcu.getTarget().os.tag) { | |
| 22732 | .vulkan, .opengl => {}, | |
| 22733 | else => return, | |
| 22734 | } | |
| 22735 | if (src_info.flags.address_space == .physical_storage_buffer) return; | |
| 22736 | ||
| 22737 | var cur: Type = .fromInterned(src_info.child); | |
| 22738 | while (cur.toIntern() != dest_info.child) { | |
| 22739 | cur = switch (cur.zigTypeTag(zcu)) { | |
| 22740 | .array, .vector => cur.childType(zcu), | |
| 22741 | .@"struct" => field: { | |
| 22742 | for (0..cur.structFieldCount(zcu)) |i| { | |
| 22743 | const field_ty = cur.fieldType(i, zcu); | |
| 22744 | if (field_ty.hasRuntimeBits(zcu) and cur.structFieldOffset(i, zcu) == 0) break :field field_ty; | |
| 22745 | } | |
| 22746 | break :field null; | |
| 22747 | }, | |
| 22748 | else => null, | |
| 22749 | } orelse return sema.failWithOwnedErrorMsg(block, msg: { | |
| 22750 | const msg = try sema.errMsg(src, "cannot cast pointer '{f}' to '{f}'", .{ operand_ty.fmt(pt), dest_ty.fmt(pt) }); | |
| 22751 | errdefer msg.destroy(sema.gpa); | |
| 22752 | try sema.errNote(src, msg, "'{f}' must appear at offset 0 inside '{f}'", .{ | |
| 22753 | Type.fromInterned(dest_info.child).fmt(pt), | |
| 22754 | Type.fromInterned(src_info.child).fmt(pt), | |
| 22755 | }); | |
| 22756 | break :msg msg; | |
| 22757 | }); | |
| 22758 | } | |
| 22759 | } | |
| 22760 | ||
| 22747 | 22761 | fn checkVectorElemType( |
| 22748 | 22762 | sema: *Sema, |
| 22749 | 22763 | block: *Block, |
| ... | ... | @@ -27367,6 +27381,7 @@ fn elemPtrOneLayerOnly( |
| 27367 | 27381 | |
| 27368 | 27382 | try sema.validateRuntimeElemAccess(block, elem_index_src, result_ty, indexable_src); |
| 27369 | 27383 | try sema.validateRuntimeValue(block, indexable_src, indexable); |
| 27384 | try sema.checkLogicalPtrOperation(block, src, indexable_ty); | |
| 27370 | 27385 | |
| 27371 | 27386 | if (child_ty.abiSize(zcu) == 0) { |
| 27372 | 27387 | // zero-bit child type; just bitcast the pointer |
| ... | ... | @@ -27439,6 +27454,7 @@ fn elemVal( |
| 27439 | 27454 | }, |
| 27440 | 27455 | .partially_comptime, .fully_comptime => unreachable, // caught by `validateRuntimeElemAccess` |
| 27441 | 27456 | } |
| 27457 | try sema.checkLogicalPtrOperation(block, src, indexable_ty); | |
| 27442 | 27458 | |
| 27443 | 27459 | return block.addBinOp(.ptr_elem_val, indexable, elem_index); |
| 27444 | 27460 | }, |
| ... | ... | @@ -27840,6 +27856,7 @@ fn elemValSlice( |
| 27840 | 27856 | const cmp_op: Air.Inst.Tag = if (slice_sent) .cmp_lte else .cmp_lt; |
| 27841 | 27857 | try sema.addSafetyCheckIndexOob(block, src, elem_index, len_inst, cmp_op); |
| 27842 | 27858 | } |
| 27859 | try sema.checkLogicalPtrOperation(block, src, slice_ty); | |
| 27843 | 27860 | return block.addBinOp(.slice_elem_val, slice, elem_index); |
| 27844 | 27861 | } |
| 27845 | 27862 | |
| ... | ... | @@ -27891,6 +27908,7 @@ fn elemPtrSlice( |
| 27891 | 27908 | |
| 27892 | 27909 | try sema.validateRuntimeElemAccess(block, elem_index_src, elem_ptr_ty, slice_src); |
| 27893 | 27910 | try sema.validateRuntimeValue(block, slice_src, slice); |
| 27911 | try sema.checkLogicalPtrOperation(block, src, slice_ty); | |
| 27894 | 27912 | |
| 27895 | 27913 | if (oob_safety and block.wantSafety()) { |
| 27896 | 27914 | const len_inst = len: { |
src/codegen/spirv/CodeGen.zig+27-4| ... | ... | @@ -2059,6 +2059,13 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 2059 | 2059 | while (cur.toIntern() != dst_child.toIntern()) { |
| 2060 | 2060 | switch (cur.zigTypeTag(zcu)) { |
| 2061 | 2061 | .array => { |
| 2062 | if (dst_child.zigTypeTag(zcu) == .array and | |
| 2063 | dst_child.childType(zcu).toIntern() == cur.childType(zcu).toIntern() and | |
| 2064 | dst_child.arrayLenIncludingSentinel(zcu) <= cur.arrayLenIncludingSentinel(zcu)) | |
| 2065 | { | |
| 2066 | cur = dst_child; | |
| 2067 | break; | |
| 2068 | } | |
| 2062 | 2069 | cur = cur.childType(zcu); |
| 2063 | 2070 | depth += 1; |
| 2064 | 2071 | }, |
| ... | ... | @@ -2098,7 +2105,7 @@ fn derivePtr(cg: *CodeGen, derivation: Value.PointerDeriveStep) !Id { |
| 2098 | 2105 | } |
| 2099 | 2106 | } |
| 2100 | 2107 | |
| 2101 | return cg.fail("cannot perform pointer cast: '{f}' to '{f}'", .{ | |
| 2108 | return cg.fail("cannot cast pointer '{f}' to '{f}'", .{ | |
| 2102 | 2109 | parent_ptr_ty.fmt(pt), |
| 2103 | 2110 | oac.new_ptr_ty.fmt(pt), |
| 2104 | 2111 | }); |
| ... | ... | @@ -6041,7 +6048,13 @@ fn bitCast( |
| 6041 | 6048 | while (cur.toIntern() != dst_child.toIntern()) : (try indices.append(gpa, 0)) { |
| 6042 | 6049 | cur = switch (cur.zigTypeTag(zcu)) { |
| 6043 | 6050 | .array, .vector => cur.childType(zcu), |
| 6044 | .@"struct" => cur.fieldType(0, zcu), | |
| 6051 | .@"struct" => field: { | |
| 6052 | for (0..cur.structFieldCount(zcu)) |i| { | |
| 6053 | const field_ty = cur.fieldType(i, zcu); | |
| 6054 | if (field_ty.hasRuntimeBits(zcu) and cur.structFieldOffset(i, zcu) == 0) break :field field_ty; | |
| 6055 | } | |
| 6056 | unreachable; | |
| 6057 | }, | |
| 6045 | 6058 | else => unreachable, |
| 6046 | 6059 | }; |
| 6047 | 6060 | } |
| ... | ... | @@ -6751,9 +6764,19 @@ fn ptrElemPtr(cg: *CodeGen, ptr_ty: Type, ptr_id: Id, index_id: Id) !Id { |
| 6751 | 6764 | const zcu = cg.zcu; |
| 6752 | 6765 | // Construct new pointer type for the resulting pointer |
| 6753 | 6766 | const as = ptr_ty.ptrAddressSpace(zcu); |
| 6754 | const elem_ty_id = try cg.pointeeType(as, ptr_ty.indexableElem(zcu), false); | |
| 6767 | const child_ty = ptr_ty.childType(zcu); | |
| 6768 | const is_single_ptr = ptr_ty.isSinglePointer(zcu); | |
| 6769 | const elem_is_block = switch (as) { | |
| 6770 | .uniform, .storage_buffer => switch (child_ty.zigTypeTag(cg.zcu)) { | |
| 6771 | .array => is_single_ptr, | |
| 6772 | .spirv => is_single_ptr and child_ty.isSpirvRuntimeArray(cg.zcu), | |
| 6773 | else => false, | |
| 6774 | }, | |
| 6775 | else => false, | |
| 6776 | }; | |
| 6777 | const elem_ty_id = try cg.pointeeType(as, ptr_ty.indexableElem(zcu), elem_is_block); | |
| 6755 | 6778 | const elem_ptr_ty_id = try cg.ptrType(elem_ty_id, cg.storageClass(as)); |
| 6756 | if (ptr_ty.isSinglePointer(zcu)) { | |
| 6779 | if (is_single_ptr) { | |
| 6757 | 6780 | // Pointer-to-array. In this case, the resulting pointer is not of the same type |
| 6758 | 6781 | // as the ptr_ty (we want a *T, not a *[N]T), and hence we need to use accessChain. |
| 6759 | 6782 | return cg.accessChainId(elem_ptr_ty_id, ptr_id, &.{index_id}); |
test/behavior/spirv.zig+5-6| ... | ... | @@ -49,13 +49,12 @@ 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 | 52 | test "@ptrCast to first field type" { |
| 57 | const pc_inner: *addrspace(.push_constant) const InnerStruct = @ptrCast(outer_pc); | |
| 58 | _ = pc_inner; | |
| 53 | const Inner = extern struct { a: u32 }; | |
| 54 | const Outer = extern struct { a: Inner, b: u32 }; | |
| 55 | var outer: Outer = undefined; | |
| 56 | var inner: *Inner = @ptrCast(&outer); | |
| 57 | _ = &inner; | |
| 59 | 58 | } |
| 60 | 59 | |
| 61 | 60 | test "@SpirvType equality" { |
test/cases/compile_errors/illegal_operation_on_logical_ptr.zig+40-4| ... | ... | @@ -22,14 +22,50 @@ export fn ptrIntArithmetic() void { |
| 22 | 22 | _ = ptr0 - 10; |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | const slice: []const u8 = "abc"; | |
| 26 | ||
| 27 | export fn sliceElemVal() void { | |
| 28 | var i: u32 = 0; | |
| 29 | _ = &i; | |
| 30 | _ = slice[i]; | |
| 31 | } | |
| 32 | ||
| 33 | export fn sliceElemPtr() void { | |
| 34 | var i: u32 = 0; | |
| 35 | _ = &i; | |
| 36 | _ = &slice[i]; | |
| 37 | } | |
| 38 | ||
| 39 | export fn manyElemVal() void { | |
| 40 | var ptr: [*]const u8 = "abc"; | |
| 41 | var i: u32 = 0; | |
| 42 | _ = .{ &ptr, &i }; | |
| 43 | _ = ptr[i]; | |
| 44 | } | |
| 45 | ||
| 46 | export fn manyElemPtr() void { | |
| 47 | var ptr: [*]const u8 = "abc"; | |
| 48 | var i: u32 = 0; | |
| 49 | _ = .{ &ptr, &i }; | |
| 50 | _ = &ptr[i]; | |
| 51 | } | |
| 52 | ||
| 25 | 53 | // error |
| 26 | 54 | // target=spirv64-vulkan |
| 27 | 55 | // |
| 28 | 56 | // :3:21: error: illegal operation on logical pointer of type '*u8' |
| 29 | // :3:21: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan | |
| 57 | // :3:21: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan | |
| 30 | 58 | // :8:20: error: illegal operation on logical pointer of type '*u8' |
| 31 | // :8:20: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan | |
| 59 | // :8:20: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan | |
| 32 | 60 | // :16:17: error: illegal operation on logical pointer of type '*u8' |
| 33 | // :16:17: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan | |
| 61 | // :16:17: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan | |
| 34 | 62 | // :22:14: error: illegal operation on logical pointer of type '[*]u8' |
| 35 | // :22:14: note: cannot perform arithmetic on pointers with address space 'generic' on target spirv-vulkan | |
| 63 | // :22:14: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan | |
| 64 | // :30:14: error: illegal operation on logical pointer of type '[]const u8' | |
| 65 | // :30:14: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan | |
| 66 | // :36:15: error: illegal operation on logical pointer of type '[]const u8' | |
| 67 | // :36:15: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan | |
| 68 | // :43:12: error: illegal operation on logical pointer of type '[*]const u8' | |
| 69 | // :43:12: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan | |
| 70 | // :50:13: error: illegal operation on logical pointer of type '[*]const u8' | |
| 71 | // :50:13: note: pointers with address space 'generic' do not support arithmetic or indexing on target spirv-vulkan |
test/cases/compile_errors/spirv_pointer_cast_requires_offset_zero.zig+7-12| ... | ... | @@ -1,20 +1,15 @@ |
| 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 | }); | |
| 1 | const Inner = extern struct { a: u32 }; | |
| 2 | const Outer = extern struct { a: u32, b: Inner }; | |
| 8 | 3 | |
| 9 | 4 | export fn main() callconv(.kernel) void { |
| 10 | const b: *addrspace(.uniform) const B = @ptrCast(a); | |
| 11 | _ = &b; | |
| 5 | var outer: Outer = undefined; | |
| 6 | const inner: *Inner = @ptrCast(&outer); | |
| 7 | _ = &inner; | |
| 12 | 8 | } |
| 13 | 9 | |
| 14 | 10 | // error |
| 15 | 11 | // backend=selfhosted |
| 16 | 12 | // target=spirv32-vulkan |
| 17 | 13 | // |
| 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 | |
| 14 | // :6:27: error: cannot cast pointer '*tmp.Outer' to '*tmp.Inner' | |
| 15 | // :6:27: note: 'tmp.Inner' must appear at offset 0 inside 'tmp.Outer' |